Merge master into breakout_swarm

Signed-off-by: Alex Ellis <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis
2018-02-01 09:25:39 +00:00
parent afeb7bbce4
commit f954bf0733
1953 changed files with 614131 additions and 175582 deletions

39
gateway/vendor/github.com/nats-io/go-nats/.gitignore generated vendored Normal file
View File

@ -0,0 +1,39 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
# Emacs
*~
\#*\#
.\#*
# vi/vim
.??*.swp
# Mac
.DS_Store
# Eclipse
.project
.settings/
# bin

21
gateway/vendor/github.com/nats-io/go-nats/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,21 @@
language: go
sudo: false
go:
- 1.9.x
- 1.8.x
- 1.7.x
install:
- go get -t ./...
- go get github.com/nats-io/gnatsd
- go get github.com/mattn/goveralls
- go get github.com/wadey/gocovmerge
- go get -u honnef.co/go/tools/cmd/megacheck
- go get -u github.com/client9/misspell/cmd/misspell
before_script:
- $(exit $(go fmt ./... | wc -l))
- go vet ./...
- misspell -error -locale US .
- megacheck -ignore "$(cat staticcheck.ignore)" ./...
script:
- go test -i -race ./...
- if [[ "$TRAVIS_GO_VERSION" == 1.7.* ]]; then ./scripts/cov.sh TRAVIS; else go test -v -race ./...; fi

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2012-2016 Apcera Inc.
Copyright (c) 2012-2017 Apcera Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

26
gateway/vendor/github.com/nats-io/go-nats/TODO.md generated vendored Normal file
View File

@ -0,0 +1,26 @@
- [ ] Better constructors, options handling
- [ ] Functions for callback settings after connection created.
- [ ] Better options for subscriptions. Slow Consumer state settable, Go routines vs Inline.
- [ ] Move off of channels for subscribers, use syncPool linkedLists, etc with highwater.
- [ ] Test for valid subjects on publish and subscribe?
- [ ] SyncSubscriber and Next for EncodedConn
- [ ] Fast Publisher?
- [ ] pooling for structs used? leaky bucket?
- [ ] Timeout 0 should work as no timeout
- [x] Ping timer
- [x] Name in Connect for gnatsd
- [x] Asynchronous error handling
- [x] Parser rewrite
- [x] Reconnect
- [x] Hide Lock
- [x] Easier encoder interface
- [x] QueueSubscribeSync
- [x] Make nats specific errors prefixed with 'nats:'
- [x] API test for closed connection
- [x] TLS/SSL
- [x] Stats collection
- [x] Disconnect detection
- [x] Optimized Publish (coalescing)
- [x] Do Examples via Go style
- [x] Standardized Errors

View File

@ -0,0 +1,354 @@
// Copyright 2016 Apcera Inc. All rights reserved.
package bench
import (
"bytes"
"encoding/csv"
"fmt"
"log"
"math"
"strconv"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/nuid"
)
// A Sample for a particular client
type Sample struct {
JobMsgCnt int
MsgCnt uint64
MsgBytes uint64
IOBytes uint64
Start time.Time
End time.Time
}
// SampleGroup for a number of samples, the group is a Sample itself agregating the values the Samples
type SampleGroup struct {
Sample
Samples []*Sample
}
// Benchmark to hold the various Samples organized by publishers and subscribers
type Benchmark struct {
Sample
Name string
RunID string
Pubs *SampleGroup
Subs *SampleGroup
subChannel chan *Sample
pubChannel chan *Sample
}
// NewBenchmark initializes a Benchmark. After creating a bench call AddSubSample/AddPubSample.
// When done collecting samples, call EndBenchmark
func NewBenchmark(name string, subCnt, pubCnt int) *Benchmark {
bm := Benchmark{Name: name, RunID: nuid.Next()}
bm.Subs = NewSampleGroup()
bm.Pubs = NewSampleGroup()
bm.subChannel = make(chan *Sample, subCnt)
bm.pubChannel = make(chan *Sample, pubCnt)
return &bm
}
// Close organizes collected Samples and calculates aggregates. After Close(), no more samples can be added.
func (bm *Benchmark) Close() {
close(bm.subChannel)
close(bm.pubChannel)
for s := range bm.subChannel {
bm.Subs.AddSample(s)
}
for s := range bm.pubChannel {
bm.Pubs.AddSample(s)
}
if bm.Subs.HasSamples() {
bm.Start = bm.Subs.Start
bm.End = bm.Subs.End
} else {
bm.Start = bm.Pubs.Start
bm.End = bm.Pubs.End
}
if bm.Subs.HasSamples() && bm.Pubs.HasSamples() {
if bm.Start.After(bm.Subs.Start) {
bm.Start = bm.Subs.Start
}
if bm.Start.After(bm.Pubs.Start) {
bm.Start = bm.Pubs.Start
}
if bm.End.Before(bm.Subs.End) {
bm.End = bm.Subs.End
}
if bm.End.Before(bm.Pubs.End) {
bm.End = bm.Pubs.End
}
}
bm.MsgBytes = bm.Pubs.MsgBytes + bm.Subs.MsgBytes
bm.IOBytes = bm.Pubs.IOBytes + bm.Subs.IOBytes
bm.MsgCnt = bm.Pubs.MsgCnt + bm.Subs.MsgCnt
bm.JobMsgCnt = bm.Pubs.JobMsgCnt + bm.Subs.JobMsgCnt
}
// AddSubSample to the benchmark
func (bm *Benchmark) AddSubSample(s *Sample) {
bm.subChannel <- s
}
// AddPubSample to the benchmark
func (bm *Benchmark) AddPubSample(s *Sample) {
bm.pubChannel <- s
}
// CSV generates a csv report of all the samples collected
func (bm *Benchmark) CSV() string {
var buffer bytes.Buffer
writer := csv.NewWriter(&buffer)
headers := []string{"#RunID", "ClientID", "MsgCount", "MsgBytes", "MsgsPerSec", "BytesPerSec", "DurationSecs"}
if err := writer.Write(headers); err != nil {
log.Fatalf("Error while serializing headers %q: %v", headers, err)
}
groups := []*SampleGroup{bm.Subs, bm.Pubs}
pre := "S"
for i, g := range groups {
if i == 1 {
pre = "P"
}
for j, c := range g.Samples {
r := []string{bm.RunID, fmt.Sprintf("%s%d", pre, j), fmt.Sprintf("%d", c.MsgCnt), fmt.Sprintf("%d", c.MsgBytes), fmt.Sprintf("%d", c.Rate()), fmt.Sprintf("%f", c.Throughput()), fmt.Sprintf("%f", c.Duration().Seconds())}
if err := writer.Write(r); err != nil {
log.Fatalf("Error while serializing %v: %v", c, err)
}
}
}
writer.Flush()
return buffer.String()
}
// NewSample creates a new Sample initialized to the provided values. The nats.Conn information captured
func NewSample(jobCount int, msgSize int, start, end time.Time, nc *nats.Conn) *Sample {
s := Sample{JobMsgCnt: jobCount, Start: start, End: end}
s.MsgBytes = uint64(msgSize * jobCount)
s.MsgCnt = nc.OutMsgs + nc.InMsgs
s.IOBytes = nc.OutBytes + nc.InBytes
return &s
}
// Throughput of bytes per second
func (s *Sample) Throughput() float64 {
return float64(s.MsgBytes) / s.Duration().Seconds()
}
// Rate of meessages in the job per second
func (s *Sample) Rate() int64 {
return int64(float64(s.JobMsgCnt) / s.Duration().Seconds())
}
func (s *Sample) String() string {
rate := commaFormat(s.Rate())
throughput := HumanBytes(s.Throughput(), false)
return fmt.Sprintf("%s msgs/sec ~ %s/sec", rate, throughput)
}
// Duration that the sample was active
func (s *Sample) Duration() time.Duration {
return s.End.Sub(s.Start)
}
// Seconds that the sample or samples were active
func (s *Sample) Seconds() float64 {
return s.Duration().Seconds()
}
// NewSampleGroup initializer
func NewSampleGroup() *SampleGroup {
s := new(SampleGroup)
s.Samples = make([]*Sample, 0)
return s
}
// Statistics information of the sample group (min, average, max and standard deviation)
func (sg *SampleGroup) Statistics() string {
return fmt.Sprintf("min %s | avg %s | max %s | stddev %s msgs", commaFormat(sg.MinRate()), commaFormat(sg.AvgRate()), commaFormat(sg.MaxRate()), commaFormat(int64(sg.StdDev())))
}
// MinRate returns the smallest message rate in the SampleGroup
func (sg *SampleGroup) MinRate() int64 {
m := int64(0)
for i, s := range sg.Samples {
if i == 0 {
m = s.Rate()
}
m = min(m, s.Rate())
}
return m
}
// MaxRate returns the largest message rate in the SampleGroup
func (sg *SampleGroup) MaxRate() int64 {
m := int64(0)
for i, s := range sg.Samples {
if i == 0 {
m = s.Rate()
}
m = max(m, s.Rate())
}
return m
}
// AvgRate returns the average of all the message rates in the SampleGroup
func (sg *SampleGroup) AvgRate() int64 {
sum := uint64(0)
for _, s := range sg.Samples {
sum += uint64(s.Rate())
}
return int64(sum / uint64(len(sg.Samples)))
}
// StdDev returns the standard deviation the message rates in the SampleGroup
func (sg *SampleGroup) StdDev() float64 {
avg := float64(sg.AvgRate())
sum := float64(0)
for _, c := range sg.Samples {
sum += math.Pow(float64(c.Rate())-avg, 2)
}
variance := sum / float64(len(sg.Samples))
return math.Sqrt(variance)
}
// AddSample adds a Sample to the SampleGroup. After adding a Sample it shouldn't be modified.
func (sg *SampleGroup) AddSample(e *Sample) {
sg.Samples = append(sg.Samples, e)
if len(sg.Samples) == 1 {
sg.Start = e.Start
sg.End = e.End
}
sg.IOBytes += e.IOBytes
sg.JobMsgCnt += e.JobMsgCnt
sg.MsgCnt += e.MsgCnt
sg.MsgBytes += e.MsgBytes
if e.Start.Before(sg.Start) {
sg.Start = e.Start
}
if e.End.After(sg.End) {
sg.End = e.End
}
}
// HasSamples returns true if the group has samples
func (sg *SampleGroup) HasSamples() bool {
return len(sg.Samples) > 0
}
// Report returns a human readable report of the samples taken in the Benchmark
func (bm *Benchmark) Report() string {
var buffer bytes.Buffer
indent := ""
if !bm.Pubs.HasSamples() && !bm.Subs.HasSamples() {
return "No publisher or subscribers. Nothing to report."
}
if bm.Pubs.HasSamples() && bm.Subs.HasSamples() {
buffer.WriteString(fmt.Sprintf("%s Pub/Sub stats: %s\n", bm.Name, bm))
indent += " "
}
if bm.Pubs.HasSamples() {
buffer.WriteString(fmt.Sprintf("%sPub stats: %s\n", indent, bm.Pubs))
if len(bm.Pubs.Samples) > 1 {
for i, stat := range bm.Pubs.Samples {
buffer.WriteString(fmt.Sprintf("%s [%d] %v (%d msgs)\n", indent, i+1, stat, stat.JobMsgCnt))
}
buffer.WriteString(fmt.Sprintf("%s %s\n", indent, bm.Pubs.Statistics()))
}
}
if bm.Subs.HasSamples() {
buffer.WriteString(fmt.Sprintf("%sSub stats: %s\n", indent, bm.Subs))
if len(bm.Subs.Samples) > 1 {
for i, stat := range bm.Subs.Samples {
buffer.WriteString(fmt.Sprintf("%s [%d] %v (%d msgs)\n", indent, i+1, stat, stat.JobMsgCnt))
}
buffer.WriteString(fmt.Sprintf("%s %s\n", indent, bm.Subs.Statistics()))
}
}
return buffer.String()
}
func commaFormat(n int64) string {
in := strconv.FormatInt(n, 10)
out := make([]byte, len(in)+(len(in)-2+int(in[0]/'0'))/3)
if in[0] == '-' {
in, out[0] = in[1:], '-'
}
for i, j, k := len(in)-1, len(out)-1, 0; ; i, j = i-1, j-1 {
out[j] = in[i]
if i == 0 {
return string(out)
}
if k++; k == 3 {
j, k = j-1, 0
out[j] = ','
}
}
}
// HumanBytes formats bytes as a human readable string
func HumanBytes(bytes float64, si bool) string {
var base = 1024
pre := []string{"K", "M", "G", "T", "P", "E"}
var post = "B"
if si {
base = 1000
pre = []string{"k", "M", "G", "T", "P", "E"}
post = "iB"
}
if bytes < float64(base) {
return fmt.Sprintf("%.2f B", bytes)
}
exp := int(math.Log(bytes) / math.Log(float64(base)))
index := exp - 1
units := pre[index] + post
return fmt.Sprintf("%.2f %s", bytes/math.Pow(float64(base), float64(exp)), units)
}
func min(x, y int64) int64 {
if x < y {
return x
}
return y
}
func max(x, y int64) int64 {
if x > y {
return x
}
return y
}
// MsgsPerClient divides the number of messages by the number of clients and tries to distribute them as evenly as possible
func MsgsPerClient(numMsgs, numClients int) []int {
var counts []int
if numClients == 0 || numMsgs == 0 {
return counts
}
counts = make([]int, numClients)
mc := numMsgs / numClients
for i := 0; i < numClients; i++ {
counts[i] = mc
}
extra := numMsgs % numClients
for i := 0; i < extra; i++ {
counts[i]++
}
return counts
}

View File

@ -0,0 +1,226 @@
package bench
import (
"fmt"
"strings"
"testing"
"time"
"github.com/nats-io/go-nats"
)
const (
MsgSize = 8
Million = 1000 * 1000
)
var baseTime = time.Now()
func millionMessagesSecondSample(seconds int) *Sample {
messages := Million * seconds
start := baseTime
end := start.Add(time.Second * time.Duration(seconds))
nc := new(nats.Conn)
s := NewSample(messages, MsgSize, start, end, nc)
s.MsgCnt = uint64(messages)
s.MsgBytes = uint64(messages * MsgSize)
s.IOBytes = s.MsgBytes
return s
}
func TestDuration(t *testing.T) {
s := millionMessagesSecondSample(1)
duration := s.End.Sub(s.Start)
if duration != s.Duration() || duration != time.Second {
t.Fatal("Expected sample duration to be 1 second")
}
}
func TestSeconds(t *testing.T) {
s := millionMessagesSecondSample(1)
seconds := s.End.Sub(s.Start).Seconds()
if seconds != s.Seconds() || seconds != 1.0 {
t.Fatal("Expected sample seconds to be 1 second")
}
}
func TestRate(t *testing.T) {
s := millionMessagesSecondSample(60)
if s.Rate() != Million {
t.Fatal("Expected rate at 1 million msgs")
}
}
func TestThoughput(t *testing.T) {
s := millionMessagesSecondSample(60)
if s.Throughput() != Million*MsgSize {
t.Fatalf("Expected throughput at %d million bytes/sec", MsgSize)
}
}
func TestStrings(t *testing.T) {
s := millionMessagesSecondSample(60)
if len(s.String()) == 0 {
t.Fatal("Sample didn't provide a String")
}
}
func TestGroupDuration(t *testing.T) {
sg := NewSampleGroup()
sg.AddSample(millionMessagesSecondSample(1))
sg.AddSample(millionMessagesSecondSample(2))
duration := sg.End.Sub(sg.Start)
if duration != sg.Duration() || duration != time.Duration(2)*time.Second {
t.Fatal("Expected aggregate duration to be 2.0 seconds")
}
}
func TestGroupSeconds(t *testing.T) {
sg := NewSampleGroup()
sg.AddSample(millionMessagesSecondSample(1))
sg.AddSample(millionMessagesSecondSample(2))
sg.AddSample(millionMessagesSecondSample(3))
seconds := sg.End.Sub(sg.Start).Seconds()
if seconds != sg.Seconds() || seconds != 3.0 {
t.Fatal("Expected aggregate seconds to be 3.0 seconds")
}
}
func TestGroupRate(t *testing.T) {
sg := NewSampleGroup()
sg.AddSample(millionMessagesSecondSample(1))
sg.AddSample(millionMessagesSecondSample(2))
sg.AddSample(millionMessagesSecondSample(3))
if sg.Rate() != Million*2 {
t.Fatal("Expected MsgRate at 2 million msg/sec")
}
}
func TestGroupThoughput(t *testing.T) {
sg := NewSampleGroup()
sg.AddSample(millionMessagesSecondSample(1))
sg.AddSample(millionMessagesSecondSample(2))
sg.AddSample(millionMessagesSecondSample(3))
if sg.Throughput() != 2*Million*MsgSize {
t.Fatalf("Expected througput at %d million bytes/sec", 2*MsgSize)
}
}
func TestMinMaxRate(t *testing.T) {
sg := NewSampleGroup()
sg.AddSample(millionMessagesSecondSample(1))
sg.AddSample(millionMessagesSecondSample(2))
sg.AddSample(millionMessagesSecondSample(3))
if sg.MinRate() != sg.MaxRate() {
t.Fatal("Expected MinRate == MaxRate")
}
}
func TestAvgRate(t *testing.T) {
sg := NewSampleGroup()
sg.AddSample(millionMessagesSecondSample(1))
sg.AddSample(millionMessagesSecondSample(2))
sg.AddSample(millionMessagesSecondSample(3))
if sg.MinRate() != sg.AvgRate() {
t.Fatal("Expected MinRate == AvgRate")
}
}
func TestStdDev(t *testing.T) {
sg := NewSampleGroup()
sg.AddSample(millionMessagesSecondSample(1))
sg.AddSample(millionMessagesSecondSample(2))
sg.AddSample(millionMessagesSecondSample(3))
if sg.StdDev() != 0.0 {
t.Fatal("Expected stddev to be zero")
}
}
func TestBenchSetup(t *testing.T) {
bench := NewBenchmark("test", 1, 1)
bench.AddSubSample(millionMessagesSecondSample(1))
bench.AddPubSample(millionMessagesSecondSample(1))
bench.Close()
if len(bench.RunID) == 0 {
t.Fatal("Bench doesn't have a RunID")
}
if len(bench.Pubs.Samples) != 1 {
t.Fatal("Expected one publisher")
}
if len(bench.Subs.Samples) != 1 {
t.Fatal("Expected one subscriber")
}
if bench.MsgCnt != 2*Million {
t.Fatal("Expected 2 million msgs")
}
if bench.IOBytes != 2*Million*MsgSize {
t.Fatalf("Expected %d million bytes", 2*MsgSize)
}
if bench.Duration() != time.Second {
t.Fatal("Expected duration to be 1 second")
}
}
func makeBench(subs, pubs int) *Benchmark {
bench := NewBenchmark("test", subs, pubs)
for i := 0; i < subs; i++ {
bench.AddSubSample(millionMessagesSecondSample(1))
}
for i := 0; i < pubs; i++ {
bench.AddPubSample(millionMessagesSecondSample(1))
}
bench.Close()
return bench
}
func TestCsv(t *testing.T) {
bench := makeBench(1, 1)
csv := bench.CSV()
lines := strings.Split(csv, "\n")
if len(lines) != 4 {
t.Fatal("Expected 4 lines of output from the CSV string")
}
fields := strings.Split(lines[1], ",")
if len(fields) != 7 {
t.Fatal("Expected 7 fields")
}
}
func TestBenchStrings(t *testing.T) {
bench := makeBench(1, 1)
s := bench.Report()
lines := strings.Split(s, "\n")
if len(lines) != 4 {
t.Fatal("Expected 3 lines of output: header, pub, sub, empty")
}
bench = makeBench(2, 2)
s = bench.Report()
lines = strings.Split(s, "\n")
if len(lines) != 10 {
fmt.Printf("%q\n", s)
t.Fatal("Expected 11 lines of output: header, pub header, pub x 2, stats, sub headers, sub x 2, stats, empty")
}
}
func TestMsgsPerClient(t *testing.T) {
zero := MsgsPerClient(0, 0)
if len(zero) != 0 {
t.Fatal("Expected 0 length for 0 clients")
}
onetwo := MsgsPerClient(1, 2)
if len(onetwo) != 2 || onetwo[0] != 1 || onetwo[1] != 0 {
t.Fatal("Expected uneven distribution")
}
twotwo := MsgsPerClient(2, 2)
if len(twotwo) != 2 || twotwo[0] != 1 || twotwo[1] != 1 {
t.Fatal("Expected even distribution")
}
threetwo := MsgsPerClient(3, 2)
if len(threetwo) != 2 || threetwo[0] != 2 || threetwo[1] != 1 {
t.Fatal("Expected uneven distribution")
}
}

257
gateway/vendor/github.com/nats-io/go-nats/enc_test.go generated vendored Normal file
View File

@ -0,0 +1,257 @@
package nats_test
import (
"fmt"
"testing"
"time"
. "github.com/nats-io/go-nats"
"github.com/nats-io/go-nats/encoders/protobuf"
"github.com/nats-io/go-nats/encoders/protobuf/testdata"
)
// Since we import above nats packages, we need to have a different
// const name than TEST_PORT that we used on the other packages.
const ENC_TEST_PORT = 8268
var options = Options{
Url: fmt.Sprintf("nats://localhost:%d", ENC_TEST_PORT),
AllowReconnect: true,
MaxReconnect: 10,
ReconnectWait: 100 * time.Millisecond,
Timeout: DefaultTimeout,
}
////////////////////////////////////////////////////////////////////////////////
// Encoded connection tests
////////////////////////////////////////////////////////////////////////////////
func TestPublishErrorAfterSubscribeDecodeError(t *testing.T) {
ts := RunServerOnPort(ENC_TEST_PORT)
defer ts.Shutdown()
opts := options
nc, _ := opts.Connect()
defer nc.Close()
c, _ := NewEncodedConn(nc, JSON_ENCODER)
//Test message type
type Message struct {
Message string
}
const testSubj = "test"
c.Subscribe(testSubj, func(msg *Message) {})
//Publish invalid json to catch decode error in subscription callback
c.Publish(testSubj, `foo`)
c.Flush()
//Next publish should be successful
if err := c.Publish(testSubj, Message{"2"}); err != nil {
t.Error("Fail to send correct json message after decode error in subscription")
}
}
func TestPublishErrorAfterInvalidPublishMessage(t *testing.T) {
ts := RunServerOnPort(ENC_TEST_PORT)
defer ts.Shutdown()
opts := options
nc, _ := opts.Connect()
defer nc.Close()
c, _ := NewEncodedConn(nc, protobuf.PROTOBUF_ENCODER)
const testSubj = "test"
c.Publish(testSubj, &testdata.Person{Name: "Anatolii"})
//Publish invalid protobuff message to catch decode error
c.Publish(testSubj, "foo")
//Next publish with valid protobuf message should be successful
if err := c.Publish(testSubj, &testdata.Person{Name: "Anatolii"}); err != nil {
t.Error("Fail to send correct protobuf message after invalid message publishing", err)
}
}
func TestVariousFailureConditions(t *testing.T) {
ts := RunServerOnPort(ENC_TEST_PORT)
defer ts.Shutdown()
dch := make(chan bool)
opts := options
opts.AsyncErrorCB = func(_ *Conn, _ *Subscription, e error) {
dch <- true
}
nc, _ := opts.Connect()
nc.Close()
if _, err := NewEncodedConn(nil, protobuf.PROTOBUF_ENCODER); err == nil {
t.Fatal("Expected an error")
}
if _, err := NewEncodedConn(nc, protobuf.PROTOBUF_ENCODER); err == nil || err != ErrConnectionClosed {
t.Fatalf("Wrong error: %v instead of %v", err, ErrConnectionClosed)
}
nc, _ = opts.Connect()
defer nc.Close()
if _, err := NewEncodedConn(nc, "foo"); err == nil {
t.Fatal("Expected an error")
}
c, err := NewEncodedConn(nc, protobuf.PROTOBUF_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
if _, err := c.Subscribe("bar", func(subj, obj string) {}); err != nil {
t.Fatalf("Unable to create subscription: %v", err)
}
if err := c.Publish("bar", &testdata.Person{Name: "Ivan"}); err != nil {
t.Fatalf("Unable to publish: %v", err)
}
if err := Wait(dch); err != nil {
t.Fatal("Did not get the async error callback")
}
if err := c.PublishRequest("foo", "bar", "foo"); err == nil {
t.Fatal("Expected an error")
}
if err := c.Request("foo", "foo", nil, 2*time.Second); err == nil {
t.Fatal("Expected an error")
}
nc.Close()
if err := c.PublishRequest("foo", "bar", &testdata.Person{Name: "Ivan"}); err == nil {
t.Fatal("Expected an error")
}
resp := &testdata.Person{}
if err := c.Request("foo", &testdata.Person{Name: "Ivan"}, resp, 2*time.Second); err == nil {
t.Fatal("Expected an error")
}
if _, err := c.Subscribe("foo", nil); err == nil {
t.Fatal("Expected an error")
}
if _, err := c.Subscribe("foo", func() {}); err == nil {
t.Fatal("Expected an error")
}
func() {
defer func() {
if r := recover(); r == nil {
t.Fatal("Expected an error")
}
}()
if _, err := c.Subscribe("foo", "bar"); err == nil {
t.Fatal("Expected an error")
}
}()
}
func TestRequest(t *testing.T) {
ts := RunServerOnPort(ENC_TEST_PORT)
defer ts.Shutdown()
dch := make(chan bool)
opts := options
nc, _ := opts.Connect()
defer nc.Close()
c, err := NewEncodedConn(nc, protobuf.PROTOBUF_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
sentName := "Ivan"
recvName := "Kozlovic"
if _, err := c.Subscribe("foo", func(_, reply string, p *testdata.Person) {
if p.Name != sentName {
t.Fatalf("Got wrong name: %v instead of %v", p.Name, sentName)
}
c.Publish(reply, &testdata.Person{Name: recvName})
dch <- true
}); err != nil {
t.Fatalf("Unable to create subscription: %v", err)
}
if _, err := c.Subscribe("foo", func(_ string, p *testdata.Person) {
if p.Name != sentName {
t.Fatalf("Got wrong name: %v instead of %v", p.Name, sentName)
}
dch <- true
}); err != nil {
t.Fatalf("Unable to create subscription: %v", err)
}
if err := c.Publish("foo", &testdata.Person{Name: sentName}); err != nil {
t.Fatalf("Unable to publish: %v", err)
}
if err := Wait(dch); err != nil {
t.Fatal("Did not get message")
}
if err := Wait(dch); err != nil {
t.Fatal("Did not get message")
}
response := &testdata.Person{}
if err := c.Request("foo", &testdata.Person{Name: sentName}, response, 2*time.Second); err != nil {
t.Fatalf("Unable to publish: %v", err)
}
if response == nil {
t.Fatal("No response received")
} else if response.Name != recvName {
t.Fatalf("Wrong response: %v instead of %v", response.Name, recvName)
}
if err := Wait(dch); err != nil {
t.Fatal("Did not get message")
}
if err := Wait(dch); err != nil {
t.Fatal("Did not get message")
}
c2, err := NewEncodedConn(nc, GOB_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c2.Close()
if _, err := c2.QueueSubscribe("bar", "baz", func(m *Msg) {
response := &Msg{Subject: m.Reply, Data: []byte(recvName)}
c2.Conn.PublishMsg(response)
dch <- true
}); err != nil {
t.Fatalf("Unable to create subscription: %v", err)
}
mReply := Msg{}
if err := c2.Request("bar", &Msg{Data: []byte(sentName)}, &mReply, 2*time.Second); err != nil {
t.Fatalf("Unable to send request: %v", err)
}
if string(mReply.Data) != recvName {
t.Fatalf("Wrong reply: %v instead of %v", string(mReply.Data), recvName)
}
if err := Wait(dch); err != nil {
t.Fatal("Did not get message")
}
if c.LastError() != nil {
t.Fatalf("Unexpected connection error: %v", c.LastError())
}
if c2.LastError() != nil {
t.Fatalf("Unexpected connection error: %v", c2.LastError())
}
}

View File

@ -0,0 +1,62 @@
// Copyright 2015 Apcera Inc. All rights reserved.
package protobuf
import (
"errors"
"github.com/golang/protobuf/proto"
"github.com/nats-io/go-nats"
)
// Additional index for registered Encoders.
const (
PROTOBUF_ENCODER = "protobuf"
)
func init() {
// Register protobuf encoder
nats.RegisterEncoder(PROTOBUF_ENCODER, &ProtobufEncoder{})
}
// ProtobufEncoder is a protobuf implementation for EncodedConn
// This encoder will use the builtin protobuf lib to Marshal
// and Unmarshal structs.
type ProtobufEncoder struct {
// Empty
}
var (
ErrInvalidProtoMsgEncode = errors.New("nats: Invalid protobuf proto.Message object passed to encode")
ErrInvalidProtoMsgDecode = errors.New("nats: Invalid protobuf proto.Message object passed to decode")
)
// Encode
func (pb *ProtobufEncoder) Encode(subject string, v interface{}) ([]byte, error) {
if v == nil {
return nil, nil
}
i, found := v.(proto.Message)
if !found {
return nil, ErrInvalidProtoMsgEncode
}
b, err := proto.Marshal(i)
if err != nil {
return nil, err
}
return b, nil
}
// Decode
func (pb *ProtobufEncoder) Decode(subject string, data []byte, vPtr interface{}) error {
if _, ok := vPtr.(*interface{}); ok {
return nil
}
i, found := vPtr.(proto.Message)
if !found {
return ErrInvalidProtoMsgDecode
}
return proto.Unmarshal(data, i)
}

View File

@ -0,0 +1,40 @@
// Code generated by protoc-gen-go.
// source: pbtest.proto
// DO NOT EDIT!
/*
Package testdata is a generated protocol buffer package.
It is generated from these files:
pbtest.proto
It has these top-level messages:
Person
*/
package testdata
import proto "github.com/golang/protobuf/proto"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
type Person struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Age int32 `protobuf:"varint,2,opt,name=age" json:"age,omitempty"`
Address string `protobuf:"bytes,3,opt,name=address" json:"address,omitempty"`
Children map[string]*Person `protobuf:"bytes,10,rep,name=children" json:"children,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
}
func (m *Person) Reset() { *m = Person{} }
func (m *Person) String() string { return proto.CompactTextString(m) }
func (*Person) ProtoMessage() {}
func (m *Person) GetChildren() map[string]*Person {
if m != nil {
return m.Children
}
return nil
}
func init() {
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
package testdata;
message Person {
string name = 1;
int32 age = 2;
string address = 3;
map<string, Person> children = 10;
}

View File

@ -0,0 +1,266 @@
package nats_test
import (
"fmt"
"time"
"github.com/nats-io/go-nats"
)
// Shows different ways to create a Conn
func ExampleConnect() {
nc, _ := nats.Connect(nats.DefaultURL)
nc.Close()
nc, _ = nats.Connect("nats://derek:secretpassword@demo.nats.io:4222")
nc.Close()
nc, _ = nats.Connect("tls://derek:secretpassword@demo.nats.io:4443")
nc.Close()
opts := nats.Options{
AllowReconnect: true,
MaxReconnect: 10,
ReconnectWait: 5 * time.Second,
Timeout: 1 * time.Second,
}
nc, _ = opts.Connect()
nc.Close()
}
// This Example shows an asynchronous subscriber.
func ExampleConn_Subscribe() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
nc.Subscribe("foo", func(m *nats.Msg) {
fmt.Printf("Received a message: %s\n", string(m.Data))
})
}
// This Example shows a synchronous subscriber.
func ExampleConn_SubscribeSync() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
sub, _ := nc.SubscribeSync("foo")
m, err := sub.NextMsg(1 * time.Second)
if err == nil {
fmt.Printf("Received a message: %s\n", string(m.Data))
} else {
fmt.Println("NextMsg timed out.")
}
}
func ExampleSubscription_NextMsg() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
sub, _ := nc.SubscribeSync("foo")
m, err := sub.NextMsg(1 * time.Second)
if err == nil {
fmt.Printf("Received a message: %s\n", string(m.Data))
} else {
fmt.Println("NextMsg timed out.")
}
}
func ExampleSubscription_Unsubscribe() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
sub, _ := nc.SubscribeSync("foo")
// ...
sub.Unsubscribe()
}
func ExampleConn_Publish() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
nc.Publish("foo", []byte("Hello World!"))
}
func ExampleConn_PublishMsg() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
msg := &nats.Msg{Subject: "foo", Reply: "bar", Data: []byte("Hello World!")}
nc.PublishMsg(msg)
}
func ExampleConn_Flush() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
msg := &nats.Msg{Subject: "foo", Reply: "bar", Data: []byte("Hello World!")}
for i := 0; i < 1000; i++ {
nc.PublishMsg(msg)
}
err := nc.Flush()
if err == nil {
// Everything has been processed by the server for nc *Conn.
}
}
func ExampleConn_FlushTimeout() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
msg := &nats.Msg{Subject: "foo", Reply: "bar", Data: []byte("Hello World!")}
for i := 0; i < 1000; i++ {
nc.PublishMsg(msg)
}
// Only wait for up to 1 second for Flush
err := nc.FlushTimeout(1 * time.Second)
if err == nil {
// Everything has been processed by the server for nc *Conn.
}
}
func ExampleConn_Request() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, []byte("I will help you"))
})
nc.Request("foo", []byte("help"), 50*time.Millisecond)
}
func ExampleConn_QueueSubscribe() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
received := 0
nc.QueueSubscribe("foo", "worker_group", func(_ *nats.Msg) {
received++
})
}
func ExampleSubscription_AutoUnsubscribe() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
received, wanted, total := 0, 10, 100
sub, _ := nc.Subscribe("foo", func(_ *nats.Msg) {
received++
})
sub.AutoUnsubscribe(wanted)
for i := 0; i < total; i++ {
nc.Publish("foo", []byte("Hello"))
}
nc.Flush()
fmt.Printf("Received = %d", received)
}
func ExampleConn_Close() {
nc, _ := nats.Connect(nats.DefaultURL)
nc.Close()
}
// Shows how to wrap a Conn into an EncodedConn
func ExampleNewEncodedConn() {
nc, _ := nats.Connect(nats.DefaultURL)
c, _ := nats.NewEncodedConn(nc, "json")
c.Close()
}
// EncodedConn can publish virtually anything just
// by passing it in. The encoder will be used to properly
// encode the raw Go type
func ExampleEncodedConn_Publish() {
nc, _ := nats.Connect(nats.DefaultURL)
c, _ := nats.NewEncodedConn(nc, "json")
defer c.Close()
type person struct {
Name string
Address string
Age int
}
me := &person{Name: "derek", Age: 22, Address: "85 Second St"}
c.Publish("hello", me)
}
// EncodedConn's subscribers will automatically decode the
// wire data into the requested Go type using the Decode()
// method of the registered Encoder. The callback signature
// can also vary to include additional data, such as subject
// and reply subjects.
func ExampleEncodedConn_Subscribe() {
nc, _ := nats.Connect(nats.DefaultURL)
c, _ := nats.NewEncodedConn(nc, "json")
defer c.Close()
type person struct {
Name string
Address string
Age int
}
c.Subscribe("hello", func(p *person) {
fmt.Printf("Received a person! %+v\n", p)
})
c.Subscribe("hello", func(subj, reply string, p *person) {
fmt.Printf("Received a person on subject %s! %+v\n", subj, p)
})
me := &person{Name: "derek", Age: 22, Address: "85 Second St"}
c.Publish("hello", me)
}
// BindSendChan() allows binding of a Go channel to a nats
// subject for publish operations. The Encoder attached to the
// EncodedConn will be used for marshaling.
func ExampleEncodedConn_BindSendChan() {
nc, _ := nats.Connect(nats.DefaultURL)
c, _ := nats.NewEncodedConn(nc, "json")
defer c.Close()
type person struct {
Name string
Address string
Age int
}
ch := make(chan *person)
c.BindSendChan("hello", ch)
me := &person{Name: "derek", Age: 22, Address: "85 Second St"}
ch <- me
}
// BindRecvChan() allows binding of a Go channel to a nats
// subject for subscribe operations. The Encoder attached to the
// EncodedConn will be used for un-marshaling.
func ExampleEncodedConn_BindRecvChan() {
nc, _ := nats.Connect(nats.DefaultURL)
c, _ := nats.NewEncodedConn(nc, "json")
defer c.Close()
type person struct {
Name string
Address string
Age int
}
ch := make(chan *person)
c.BindRecvChan("hello", ch)
me := &person{Name: "derek", Age: 22, Address: "85 Second St"}
c.Publish("hello", me)
// Receive the publish directly on a channel
who := <-ch
fmt.Printf("%v says hello!\n", who)
}

View File

@ -0,0 +1,146 @@
// Copyright 2015 Apcera Inc. All rights reserved.
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"strings"
"sync"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/go-nats/bench"
)
// Some sane defaults
const (
DefaultNumMsgs = 100000
DefaultNumPubs = 1
DefaultNumSubs = 0
DefaultMessageSize = 128
)
func usage() {
log.Fatalf("Usage: nats-bench [-s server (%s)] [--tls] [-np NUM_PUBLISHERS] [-ns NUM_SUBSCRIBERS] [-n NUM_MSGS] [-ms MESSAGE_SIZE] [-csv csvfile] <subject>\n", nats.DefaultURL)
}
var benchmark *bench.Benchmark
func main() {
var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")
var tls = flag.Bool("tls", false, "Use TLS Secure Connection")
var numPubs = flag.Int("np", DefaultNumPubs, "Number of Concurrent Publishers")
var numSubs = flag.Int("ns", DefaultNumSubs, "Number of Concurrent Subscribers")
var numMsgs = flag.Int("n", DefaultNumMsgs, "Number of Messages to Publish")
var msgSize = flag.Int("ms", DefaultMessageSize, "Size of the message.")
var csvFile = flag.String("csv", "", "Save bench data to csv file")
log.SetFlags(0)
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) != 1 {
usage()
}
if *numMsgs <= 0 {
log.Fatal("Number of messages should be greater than zero.")
}
// Setup the option block
opts := nats.GetDefaultOptions()
opts.Servers = strings.Split(*urls, ",")
for i, s := range opts.Servers {
opts.Servers[i] = strings.Trim(s, " ")
}
opts.Secure = *tls
benchmark = bench.NewBenchmark("NATS", *numSubs, *numPubs)
var startwg sync.WaitGroup
var donewg sync.WaitGroup
donewg.Add(*numPubs + *numSubs)
// Run Subscribers first
startwg.Add(*numSubs)
for i := 0; i < *numSubs; i++ {
go runSubscriber(&startwg, &donewg, opts, *numMsgs, *msgSize)
}
startwg.Wait()
// Now Publishers
startwg.Add(*numPubs)
pubCounts := bench.MsgsPerClient(*numMsgs, *numPubs)
for i := 0; i < *numPubs; i++ {
go runPublisher(&startwg, &donewg, opts, pubCounts[i], *msgSize)
}
log.Printf("Starting benchmark [msgs=%d, msgsize=%d, pubs=%d, subs=%d]\n", *numMsgs, *msgSize, *numPubs, *numSubs)
startwg.Wait()
donewg.Wait()
benchmark.Close()
fmt.Print(benchmark.Report())
if len(*csvFile) > 0 {
csv := benchmark.CSV()
ioutil.WriteFile(*csvFile, []byte(csv), 0644)
fmt.Printf("Saved metric data in csv file %s\n", *csvFile)
}
}
func runPublisher(startwg, donewg *sync.WaitGroup, opts nats.Options, numMsgs int, msgSize int) {
nc, err := opts.Connect()
if err != nil {
log.Fatalf("Can't connect: %v\n", err)
}
defer nc.Close()
startwg.Done()
args := flag.Args()
subj := args[0]
var msg []byte
if msgSize > 0 {
msg = make([]byte, msgSize)
}
start := time.Now()
for i := 0; i < numMsgs; i++ {
nc.Publish(subj, msg)
}
nc.Flush()
benchmark.AddPubSample(bench.NewSample(numMsgs, msgSize, start, time.Now(), nc))
donewg.Done()
}
func runSubscriber(startwg, donewg *sync.WaitGroup, opts nats.Options, numMsgs int, msgSize int) {
nc, err := opts.Connect()
if err != nil {
log.Fatalf("Can't connect: %v\n", err)
}
args := flag.Args()
subj := args[0]
received := 0
start := time.Now()
nc.Subscribe(subj, func(msg *nats.Msg) {
received++
if received >= numMsgs {
benchmark.AddSubSample(bench.NewSample(numMsgs, msgSize, start, time.Now(), nc))
donewg.Done()
nc.Close()
}
})
nc.Flush()
startwg.Done()
}

View File

@ -0,0 +1,46 @@
// Copyright 2012-2016 Apcera Inc. All rights reserved.
// +build ignore
package main
import (
"flag"
"log"
"github.com/nats-io/go-nats"
)
// NOTE: Use tls scheme for TLS, e.g. nats-pub -s tls://demo.nats.io:4443 foo hello
func usage() {
log.Fatalf("Usage: nats-pub [-s server (%s)] <subject> <msg> \n", nats.DefaultURL)
}
func main() {
var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")
log.SetFlags(0)
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) < 2 {
usage()
}
nc, err := nats.Connect(*urls)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
subj, msg := args[0], []byte(args[1])
nc.Publish(subj, msg)
nc.Flush()
if err := nc.LastError(); err != nil {
log.Fatal(err)
} else {
log.Printf("Published [%s] : '%s'\n", subj, msg)
}
}

View File

@ -0,0 +1,60 @@
// Copyright 2012-2016 Apcera Inc. All rights reserved.
// +build ignore
package main
import (
"flag"
"log"
"os"
"runtime"
"github.com/nats-io/go-nats"
)
// NOTE: Use tls scheme for TLS, e.g. nats-qsub -s tls://demo.nats.io:4443 foo
func usage() {
log.Fatalf("Usage: nats-qsub [-s server] [-t] <subject> <queue-group>\n")
}
func printMsg(m *nats.Msg, i int) {
log.Printf("[#%d] Received on [%s] Queue[%s] Pid[%d]: '%s'\n", i, m.Subject, m.Sub.Queue, os.Getpid(), string(m.Data))
}
func main() {
var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")
var showTime = flag.Bool("t", false, "Display timestamps")
log.SetFlags(0)
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) < 2 {
usage()
}
nc, err := nats.Connect(*urls)
if err != nil {
log.Fatalf("Can't connect: %v\n", err)
}
subj, queue, i := args[0], args[1], 0
nc.QueueSubscribe(subj, queue, func(msg *nats.Msg) {
i++
printMsg(msg, i)
})
nc.Flush()
if err := nc.LastError(); err != nil {
log.Fatal(err)
}
log.Printf("Listening on [%s]\n", subj)
if *showTime {
log.SetFlags(log.LstdFlags)
}
runtime.Goexit()
}

View File

@ -0,0 +1,48 @@
// Copyright 2012-2016 Apcera Inc. All rights reserved.
// +build ignore
package main
import (
"flag"
"log"
"time"
"github.com/nats-io/go-nats"
)
// NOTE: Use tls scheme for TLS, e.g. nats-req -s tls://demo.nats.io:4443 foo hello
func usage() {
log.Fatalf("Usage: nats-req [-s server (%s)] <subject> <msg> \n", nats.DefaultURL)
}
func main() {
var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")
log.SetFlags(0)
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) < 2 {
usage()
}
nc, err := nats.Connect(*urls)
if err != nil {
log.Fatalf("Can't connect: %v\n", err)
}
defer nc.Close()
subj, payload := args[0], []byte(args[1])
msg, err := nc.Request(subj, []byte(payload), 100*time.Millisecond)
if err != nil {
if nc.LastError() != nil {
log.Fatalf("Error in Request: %v\n", nc.LastError())
}
log.Fatalf("Error in Request: %v\n", err)
}
log.Printf("Published [%s] : '%s'\n", subj, payload)
log.Printf("Received [%v] : '%s'\n", msg.Subject, string(msg.Data))
}

View File

@ -0,0 +1,60 @@
// Copyright 2012-2016 Apcera Inc. All rights reserved.
// +build ignore
package main
import (
"flag"
"log"
"runtime"
"github.com/nats-io/go-nats"
)
// NOTE: Use tls scheme for TLS, e.g. nats-rply -s tls://demo.nats.io:4443 foo hello
func usage() {
log.Fatalf("Usage: nats-rply [-s server][-t] <subject> <response>\n")
}
func printMsg(m *nats.Msg, i int) {
log.Printf("[#%d] Received on [%s]: '%s'\n", i, m.Subject, string(m.Data))
}
func main() {
var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")
var showTime = flag.Bool("t", false, "Display timestamps")
log.SetFlags(0)
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) < 2 {
usage()
}
nc, err := nats.Connect(*urls)
if err != nil {
log.Fatalf("Can't connect: %v\n", err)
}
subj, reply, i := args[0], args[1], 0
nc.Subscribe(subj, func(msg *nats.Msg) {
i++
printMsg(msg, i)
nc.Publish(msg.Reply, []byte(reply))
})
nc.Flush()
if err := nc.LastError(); err != nil {
log.Fatal(err)
}
log.Printf("Listening on [%s]\n", subj)
if *showTime {
log.SetFlags(log.LstdFlags)
}
runtime.Goexit()
}

View File

@ -0,0 +1,59 @@
// Copyright 2012-2016 Apcera Inc. All rights reserved.
// +build ignore
package main
import (
"flag"
"log"
"runtime"
"github.com/nats-io/go-nats"
)
// NOTE: Use tls scheme for TLS, e.g. nats-sub -s tls://demo.nats.io:4443 foo
func usage() {
log.Fatalf("Usage: nats-sub [-s server] [-t] <subject> \n")
}
func printMsg(m *nats.Msg, i int) {
log.Printf("[#%d] Received on [%s]: '%s'\n", i, m.Subject, string(m.Data))
}
func main() {
var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")
var showTime = flag.Bool("t", false, "Display timestamps")
log.SetFlags(0)
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) < 1 {
usage()
}
nc, err := nats.Connect(*urls)
if err != nil {
log.Fatalf("Can't connect: %v\n", err)
}
subj, i := args[0], 0
nc.Subscribe(subj, func(msg *nats.Msg) {
i += 1
printMsg(msg, i)
})
nc.Flush()
if err := nc.LastError(); err != nil {
log.Fatal(err)
}
log.Printf("Listening on [%s]\n", subj)
if *showTime {
log.SetFlags(log.LstdFlags)
}
runtime.Goexit()
}

View File

@ -123,6 +123,12 @@ type asyncCB func()
// Option is a function on the options for a connection.
type Option func(*Options) error
// CustomDialer can be used to specify any dialer, not necessarily
// a *net.Dialer.
type CustomDialer interface {
Dial(network, address string) (net.Conn, error)
}
// Options can be used to create a customized connection.
type Options struct {
@ -225,9 +231,14 @@ type Options struct {
// Token sets the token to be used when connecting to a server.
Token string
// Dialer allows a custom Dialer when forming connections.
// Dialer allows a custom net.Dialer when forming connections.
// DEPRECATED: should use CustomDialer instead.
Dialer *net.Dialer
// CustomDialer allows to specify a custom dialer (not necessarily
// a *net.Dialer).
CustomDialer CustomDialer
// UseOldRequestStyle forces the old method of Requests that utilize
// a new Inbox and a new Subscription for each request.
UseOldRequestStyle bool
@ -450,7 +461,7 @@ func RootCAs(file ...string) Option {
if err != nil || rootPEM == nil {
return fmt.Errorf("nats: error loading or parsing rootCA file: %v", err)
}
ok := pool.AppendCertsFromPEM([]byte(rootPEM))
ok := pool.AppendCertsFromPEM(rootPEM)
if !ok {
return fmt.Errorf("nats: failed to parse root certificate from %q", f)
}
@ -586,6 +597,7 @@ func Token(token string) Option {
// Dialer is an Option to set the dialer which will be used when
// attempting to establish a connection.
// DEPRECATED: Should use CustomDialer instead.
func Dialer(dialer *net.Dialer) Option {
return func(o *Options) error {
o.Dialer = dialer
@ -593,6 +605,16 @@ func Dialer(dialer *net.Dialer) Option {
}
}
// SetCustomDialer is an Option to set a custom dialer which will be
// used when attempting to establish a connection. If both Dialer
// and CustomDialer are specified, CustomDialer takes precedence.
func SetCustomDialer(dialer CustomDialer) Option {
return func(o *Options) error {
o.CustomDialer = dialer
return nil
}
}
// UseOldRequestyStyle is an Option to force usage of the old Request style.
func UseOldRequestStyle() Option {
return func(o *Options) error {
@ -787,7 +809,7 @@ const tlsScheme = "tls"
// Create the server pool using the options given.
// We will place a Url option first, followed by any
// Server Options. We will randomize the server pool unlesss
// Server Options. We will randomize the server pool unless
// the NoRandomize flag is set.
func (nc *Conn) setupServerPool() error {
nc.srvPool = make([]*srv, 0, srvPoolSize)
@ -877,7 +899,13 @@ func (nc *Conn) createConn() (err error) {
cur.lastAttempt = time.Now()
}
dialer := nc.Opts.Dialer
// CustomDialer takes precedence. If not set, use Opts.Dialer which
// is set to a default *net.Dialer (in Connect()) if not explicitly
// set by the user.
dialer := nc.Opts.CustomDialer
if dialer == nil {
dialer = nc.Opts.Dialer
}
nc.conn, err = dialer.Dial("tcp", nc.url.Host)
if err != nil {
return err
@ -1040,7 +1068,7 @@ func (nc *Conn) connect() error {
// to connect immediately.
nc.mu.Lock()
nc.initc = true
// The pool may change inside theloop iteration due to INFO protocol.
// The pool may change inside the loop iteration due to INFO protocol.
for i := 0; i < len(nc.srvPool); i++ {
nc.url = nc.srvPool[i].url
@ -1682,9 +1710,11 @@ slowConsumer:
// permissions violation on either publish or subscribe.
func (nc *Conn) processPermissionsViolation(err string) {
nc.mu.Lock()
nc.err = errors.New("nats: " + err)
// create error here so we can pass it as a closure to the async cb dispatcher.
e := errors.New("nats: " + err)
nc.err = e
if nc.Opts.AsyncErrorCB != nil {
nc.ach <- func() { nc.Opts.AsyncErrorCB(nc, nil, nc.err) }
nc.ach <- func() { nc.Opts.AsyncErrorCB(nc, nil, e) }
}
nc.mu.Unlock()
}
@ -2248,7 +2278,7 @@ func (nc *Conn) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg) (*Sub
// We will send these for all subs when we reconnect
// so that we can suppress here.
if !nc.isReconnecting() {
nc.bw.WriteString(fmt.Sprintf(subProto, subj, queue, sub.sid))
fmt.Fprintf(nc.bw, subProto, subj, queue, sub.sid)
}
return sub, nil
}
@ -2368,7 +2398,7 @@ func (nc *Conn) unsubscribe(sub *Subscription, max int) error {
// We will send these for all subs when we reconnect
// so that we can suppress here.
if !nc.isReconnecting() {
nc.bw.WriteString(fmt.Sprintf(unsubProto, s.sid, maxStr))
fmt.Fprintf(nc.bw, unsubProto, s.sid, maxStr)
}
return nil
}
@ -2670,7 +2700,10 @@ func (nc *Conn) FlushTimeout(timeout time.Duration) (err error) {
t := globalTimerPool.Get(timeout)
defer globalTimerPool.Put(t)
ch := make(chan struct{})
// Create a buffered channel to prevent chan send to block
// in processPong() if this code here times out just when
// PONG was received.
ch := make(chan struct{}, 1)
nc.sendPing(ch)
nc.mu.Unlock()
@ -2732,16 +2765,16 @@ func (nc *Conn) resendSubscriptions() {
// reached the max, if so unsubscribe.
if adjustedMax == 0 {
s.mu.Unlock()
nc.bw.WriteString(fmt.Sprintf(unsubProto, s.sid, _EMPTY_))
fmt.Fprintf(nc.bw, unsubProto, s.sid, _EMPTY_)
continue
}
}
s.mu.Unlock()
nc.bw.WriteString(fmt.Sprintf(subProto, s.Subject, s.Queue, s.sid))
fmt.Fprintf(nc.bw, subProto, s.Subject, s.Queue, s.sid)
if adjustedMax > 0 {
maxStr := strconv.Itoa(int(adjustedMax))
nc.bw.WriteString(fmt.Sprintf(unsubProto, s.sid, maxStr))
fmt.Fprintf(nc.bw, unsubProto, s.sid, maxStr)
}
}
}

1177
gateway/vendor/github.com/nats-io/go-nats/nats_test.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

19
gateway/vendor/github.com/nats-io/go-nats/scripts/cov.sh generated vendored Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash -e
# Run from directory above via ./scripts/cov.sh
rm -rf ./cov
mkdir cov
go test -v -race -covermode=atomic -coverprofile=./cov/nats.out
go test -v -race -covermode=atomic -coverprofile=./cov/test.out -coverpkg=github.com/nats-io/go-nats ./test
go test -v -race -covermode=atomic -coverprofile=./cov/builtin.out -coverpkg=github.com/nats-io/go-nats/encoders/builtin ./test -run EncBuiltin
go test -v -race -covermode=atomic -coverprofile=./cov/protobuf.out -coverpkg=github.com/nats-io/go-nats/encoders/protobuf ./test -run EncProto
gocovmerge ./cov/*.out > acc.out
rm -rf ./cov
# If we have an arg, assume travis run and push to coveralls. Otherwise launch browser results
if [[ -n $1 ]]; then
$HOME/gopath/bin/goveralls -coverprofile=acc.out -service travis-ci
rm -rf ./acc.out
else
go tool cover -html=acc.out
fi

View File

@ -0,0 +1,4 @@
github.com/nats-io/go-nats/*_test.go:SA2002
github.com/nats-io/go-nats/*/*_test.go:SA2002
github.com/nats-io/go-nats/test/context_test.go:SA1012
github.com/nats-io/go-nats/nats.go:SA6000

View File

@ -0,0 +1,223 @@
package test
import (
"fmt"
"strings"
"sync/atomic"
"testing"
"time"
"github.com/nats-io/gnatsd/server"
"github.com/nats-io/gnatsd/test"
"github.com/nats-io/go-nats"
)
func TestAuth(t *testing.T) {
opts := test.DefaultTestOptions
opts.Port = 8232
opts.Username = "derek"
opts.Password = "foo"
s := RunServerWithOptions(opts)
defer s.Shutdown()
_, err := nats.Connect("nats://localhost:8232")
if err == nil {
t.Fatal("Should have received an error while trying to connect")
}
// This test may be a bit too strict for the future, but for now makes
// sure that we correctly process the -ERR content on connect.
if err.Error() != nats.ErrAuthorization.Error() {
t.Fatalf("Expected error '%v', got '%v'", nats.ErrAuthorization, err)
}
nc, err := nats.Connect("nats://derek:foo@localhost:8232")
if err != nil {
t.Fatal("Should have connected successfully with a token")
}
nc.Close()
// Use Options
nc, err = nats.Connect("nats://localhost:8232", nats.UserInfo("derek", "foo"))
if err != nil {
t.Fatalf("Should have connected successfully with a token: %v", err)
}
nc.Close()
// Verify that credentials in URL take precedence.
nc, err = nats.Connect("nats://derek:foo@localhost:8232", nats.UserInfo("foo", "bar"))
if err != nil {
t.Fatalf("Should have connected successfully with a token: %v", err)
}
nc.Close()
}
func TestAuthFailNoDisconnectCB(t *testing.T) {
opts := test.DefaultTestOptions
opts.Port = 8232
opts.Username = "derek"
opts.Password = "foo"
s := RunServerWithOptions(opts)
defer s.Shutdown()
copts := nats.GetDefaultOptions()
copts.Url = "nats://localhost:8232"
receivedDisconnectCB := int32(0)
copts.DisconnectedCB = func(nc *nats.Conn) {
atomic.AddInt32(&receivedDisconnectCB, 1)
}
_, err := copts.Connect()
if err == nil {
t.Fatal("Should have received an error while trying to connect")
}
if atomic.LoadInt32(&receivedDisconnectCB) > 0 {
t.Fatal("Should not have received a disconnect callback on auth failure")
}
}
func TestAuthFailAllowReconnect(t *testing.T) {
ts := RunServerOnPort(23232)
defer ts.Shutdown()
var servers = []string{
"nats://localhost:23232",
"nats://localhost:23233",
"nats://localhost:23234",
}
ots2 := test.DefaultTestOptions
ots2.Port = 23233
ots2.Username = "ivan"
ots2.Password = "foo"
ts2 := RunServerWithOptions(ots2)
defer ts2.Shutdown()
ts3 := RunServerOnPort(23234)
defer ts3.Shutdown()
reconnectch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Servers = servers
opts.AllowReconnect = true
opts.NoRandomize = true
opts.MaxReconnect = 10
opts.ReconnectWait = 100 * time.Millisecond
opts.ReconnectedCB = func(_ *nats.Conn) {
reconnectch <- true
}
// Connect
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
// Stop the server
ts.Shutdown()
// The client will try to connect to the second server, and that
// should fail. It should then try to connect to the third and succeed.
// Wait for the reconnect CB.
if e := Wait(reconnectch); e != nil {
t.Fatal("Reconnect callback should have been triggered")
}
if nc.IsClosed() {
t.Fatal("Should have reconnected")
}
if nc.ConnectedUrl() != servers[2] {
t.Fatalf("Should have reconnected to %s, reconnected to %s instead", servers[2], nc.ConnectedUrl())
}
}
func TestTokenAuth(t *testing.T) {
opts := test.DefaultTestOptions
opts.Port = 8232
secret := "S3Cr3T0k3n!"
opts.Authorization = secret
s := RunServerWithOptions(opts)
defer s.Shutdown()
_, err := nats.Connect("nats://localhost:8232")
if err == nil {
t.Fatal("Should have received an error while trying to connect")
}
tokenURL := fmt.Sprintf("nats://%s@localhost:8232", secret)
nc, err := nats.Connect(tokenURL)
if err != nil {
t.Fatal("Should have connected successfully")
}
nc.Close()
// Use Options
nc, err = nats.Connect("nats://localhost:8232", nats.Token(secret))
if err != nil {
t.Fatalf("Should have connected successfully: %v", err)
}
nc.Close()
// Verify that token in the URL takes precedence.
nc, err = nats.Connect(tokenURL, nats.Token("badtoken"))
if err != nil {
t.Fatalf("Should have connected successfully: %v", err)
}
nc.Close()
}
func TestPermViolation(t *testing.T) {
opts := test.DefaultTestOptions
opts.Port = 8232
opts.Users = []*server.User{
&server.User{
Username: "ivan",
Password: "pwd",
Permissions: &server.Permissions{
Publish: []string{"foo"},
Subscribe: []string{"bar"},
},
},
}
s := RunServerWithOptions(opts)
defer s.Shutdown()
errCh := make(chan error, 2)
errCB := func(_ *nats.Conn, _ *nats.Subscription, err error) {
errCh <- err
}
nc, err := nats.Connect(
fmt.Sprintf("nats://ivan:pwd@localhost:%d", opts.Port),
nats.ErrorHandler(errCB))
if err != nil {
t.Fatalf("Error on connect: %v", err)
}
defer nc.Close()
// Cause a publish error
nc.Publish("bar", []byte("fail"))
// Cause a subscribe error
nc.Subscribe("foo", func(_ *nats.Msg) {})
expectedErrorTypes := []string{"publish", "subscription"}
for _, expectedErr := range expectedErrorTypes {
select {
case e := <-errCh:
if !strings.Contains(e.Error(), nats.PERMISSIONS_ERR) {
t.Fatalf("Did not receive error about permissions")
}
if !strings.Contains(e.Error(), expectedErr) {
t.Fatalf("Did not receive error about %q, got %v", expectedErr, e.Error())
}
case <-time.After(2 * time.Second):
t.Fatalf("Did not get the permission error")
}
}
// Make sure connection has not been closed
if nc.IsClosed() {
t.Fatal("Connection should be not be closed")
}
}

View File

@ -0,0 +1,885 @@
package test
import (
"bytes"
"math"
"regexp"
"runtime"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/nats-io/go-nats"
)
func TestCloseLeakingGoRoutines(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
base := runtime.NumGoroutine()
nc := NewDefaultConnection(t)
nc.Flush()
nc.Close()
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - base)
if delta > 0 {
t.Fatalf("%d Go routines still exist post Close()", delta)
}
// Make sure we can call Close() multiple times
nc.Close()
}
func TestLeakingGoRoutinesOnFailedConnect(t *testing.T) {
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
base := runtime.NumGoroutine()
nc, err := nats.Connect(nats.DefaultURL)
if err == nil {
nc.Close()
t.Fatalf("Expected failure to connect")
}
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - base)
if delta > 0 {
t.Fatalf("%d Go routines still exist post Close()", delta)
}
}
func TestConnectedServer(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
u := nc.ConnectedUrl()
if u == "" || u != nats.DefaultURL {
t.Fatalf("Unexpected connected URL of %s\n", u)
}
srv := nc.ConnectedServerId()
if srv == "" {
t.Fatal("Expected a connected server id")
}
nc.Close()
u = nc.ConnectedUrl()
if u != "" {
t.Fatalf("Expected a nil connected URL, got %s\n", u)
}
srv = nc.ConnectedServerId()
if srv != "" {
t.Fatalf("Expected a nil connect server, got %s\n", srv)
}
}
func TestMultipleClose(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
nc.Close()
wg.Done()
}()
}
wg.Wait()
}
func TestBadOptionTimeoutConnect(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
opts := nats.GetDefaultOptions()
opts.Timeout = -1
opts.Url = "nats://localhost:4222"
_, err := opts.Connect()
if err == nil {
t.Fatal("Expected an error")
}
if err != nats.ErrNoServers {
t.Fatalf("Expected a ErrNoServers error: Got %v\n", err)
}
}
func TestSimplePublish(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
if err := nc.Publish("foo", []byte("Hello World")); err != nil {
t.Fatal("Failed to publish string message: ", err)
}
}
func TestSimplePublishNoData(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
if err := nc.Publish("foo", nil); err != nil {
t.Fatal("Failed to publish empty message: ", err)
}
}
func TestPublishDoesNotFailOnSlowConsumer(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
sub, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatalf("Unable to create subscription: %v", err)
}
if err := sub.SetPendingLimits(1, 1000); err != nil {
t.Fatalf("Unable to set pending limits: %v", err)
}
var pubErr error
msg := []byte("Hello")
for i := 0; i < 10; i++ {
pubErr = nc.Publish("foo", msg)
if pubErr != nil {
break
}
nc.Flush()
}
if pubErr != nil {
t.Fatalf("Publish() should not fail because of slow consumer. Got '%v'", pubErr)
}
}
func TestAsyncSubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
omsg := []byte("Hello World")
ch := make(chan bool)
// Callback is mandatory
if _, err := nc.Subscribe("foo", nil); err == nil {
t.Fatal("Creating subscription without callback should have failed")
}
_, err := nc.Subscribe("foo", func(m *nats.Msg) {
if !bytes.Equal(m.Data, omsg) {
t.Fatal("Message received does not match")
}
if m.Sub == nil {
t.Fatal("Callback does not have a valid Subscription")
}
ch <- true
})
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
nc.Publish("foo", omsg)
if e := Wait(ch); e != nil {
t.Fatal("Message not received for subscription")
}
}
func TestAsyncSubscribeRoutineLeakOnUnsubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ch := make(chan bool)
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
// Take the base once the connection is established, but before
// the subscriber is created.
base := runtime.NumGoroutine()
sub, err := nc.Subscribe("foo", func(m *nats.Msg) { ch <- true })
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
// Send to ourself
nc.Publish("foo", []byte("hello"))
// This ensures that the async delivery routine is up and running.
if err := Wait(ch); err != nil {
t.Fatal("Failed to receive message")
}
// Make sure to give it time to go back into wait
time.Sleep(200 * time.Millisecond)
// Explicit unsubscribe
sub.Unsubscribe()
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - base)
if delta > 0 {
t.Fatalf("%d Go routines still exist post Unsubscribe()", delta)
}
}
func TestAsyncSubscribeRoutineLeakOnClose(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ch := make(chan bool)
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
// Take the base before creating the connection, since we are going
// to close it before taking the delta.
base := runtime.NumGoroutine()
nc := NewDefaultConnection(t)
defer nc.Close()
_, err := nc.Subscribe("foo", func(m *nats.Msg) { ch <- true })
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
// Send to ourself
nc.Publish("foo", []byte("hello"))
// This ensures that the async delivery routine is up and running.
if err := Wait(ch); err != nil {
t.Fatal("Failed to receive message")
}
// Make sure to give it time to go back into wait
time.Sleep(200 * time.Millisecond)
// Close connection without explicit unsubscribe
nc.Close()
// Give time for things to settle before capturing the number of
// go routines
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - base)
if delta > 0 {
t.Fatalf("%d Go routines still exist post Close()", delta)
}
}
func TestSyncSubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
sub, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
omsg := []byte("Hello World")
nc.Publish("foo", omsg)
msg, err := sub.NextMsg(1 * time.Second)
if err != nil || !bytes.Equal(msg.Data, omsg) {
t.Fatal("Message received does not match")
}
}
func TestPubSubWithReply(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
sub, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
omsg := []byte("Hello World")
nc.PublishMsg(&nats.Msg{Subject: "foo", Reply: "bar", Data: omsg})
msg, err := sub.NextMsg(10 * time.Second)
if err != nil || !bytes.Equal(msg.Data, omsg) {
t.Fatal("Message received does not match")
}
}
func TestFlush(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
omsg := []byte("Hello World")
for i := 0; i < 10000; i++ {
nc.Publish("flush", omsg)
}
if err := nc.FlushTimeout(0); err == nil {
t.Fatal("Calling FlushTimeout() with invalid timeout should fail")
}
if err := nc.Flush(); err != nil {
t.Fatalf("Received error from flush: %s\n", err)
}
if nb, _ := nc.Buffered(); nb > 0 {
t.Fatalf("Outbound buffer not empty: %d bytes\n", nb)
}
nc.Close()
if _, err := nc.Buffered(); err == nil {
t.Fatal("Calling Buffered() on closed connection should fail")
}
}
func TestQueueSubscriber(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
s1, _ := nc.QueueSubscribeSync("foo", "bar")
s2, _ := nc.QueueSubscribeSync("foo", "bar")
omsg := []byte("Hello World")
nc.Publish("foo", omsg)
nc.Flush()
r1, _ := s1.QueuedMsgs()
r2, _ := s2.QueuedMsgs()
if (r1 + r2) != 1 {
t.Fatal("Received too many messages for multiple queue subscribers")
}
// Drain messages
s1.NextMsg(time.Second)
s2.NextMsg(time.Second)
total := 1000
for i := 0; i < total; i++ {
nc.Publish("foo", omsg)
}
nc.Flush()
v := uint(float32(total) * 0.15)
r1, _ = s1.QueuedMsgs()
r2, _ = s2.QueuedMsgs()
if r1+r2 != total {
t.Fatalf("Incorrect number of messages: %d vs %d", (r1 + r2), total)
}
expected := total / 2
d1 := uint(math.Abs(float64(expected - r1)))
d2 := uint(math.Abs(float64(expected - r2)))
if d1 > v || d2 > v {
t.Fatalf("Too much variance in totals: %d, %d > %d", d1, d2, v)
}
}
func TestReplyArg(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ch := make(chan bool)
replyExpected := "bar"
nc.Subscribe("foo", func(m *nats.Msg) {
if m.Reply != replyExpected {
t.Fatalf("Did not receive correct reply arg in callback: "+
"('%s' vs '%s')", m.Reply, replyExpected)
}
ch <- true
})
nc.PublishMsg(&nats.Msg{Subject: "foo", Reply: replyExpected, Data: []byte("Hello")})
if e := Wait(ch); e != nil {
t.Fatal("Did not receive callback")
}
}
func TestSyncReplyArg(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
replyExpected := "bar"
sub, _ := nc.SubscribeSync("foo")
nc.PublishMsg(&nats.Msg{Subject: "foo", Reply: replyExpected, Data: []byte("Hello")})
msg, err := sub.NextMsg(1 * time.Second)
if err != nil {
t.Fatal("Received an err on NextMsg()")
}
if msg.Reply != replyExpected {
t.Fatalf("Did not receive correct reply arg in callback: "+
"('%s' vs '%s')", msg.Reply, replyExpected)
}
}
func TestUnsubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
received := int32(0)
max := int32(10)
ch := make(chan bool)
nc.Subscribe("foo", func(m *nats.Msg) {
atomic.AddInt32(&received, 1)
if received == max {
err := m.Sub.Unsubscribe()
if err != nil {
t.Fatal("Unsubscribe failed with err:", err)
}
ch <- true
}
})
send := 20
for i := 0; i < send; i++ {
nc.Publish("foo", []byte("hello"))
}
nc.Flush()
<-ch
r := atomic.LoadInt32(&received)
if r != max {
t.Fatalf("Received wrong # of messages after unsubscribe: %d vs %d",
r, max)
}
}
func TestDoubleUnsubscribe(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
sub, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatal("Failed to subscribe: ", err)
}
if err = sub.Unsubscribe(); err != nil {
t.Fatal("Unsubscribe failed with err:", err)
}
if err = sub.Unsubscribe(); err == nil {
t.Fatal("Unsubscribe should have reported an error")
}
}
func TestRequestTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
if _, err := nc.Request("foo", []byte("help"), 10*time.Millisecond); err == nil {
t.Fatalf("Expected to receive a timeout error")
}
}
func TestOldRequest(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
})
msg, err := nc.Request("foo", []byte("help"), 500*time.Millisecond)
if err != nil {
t.Fatalf("Received an error on Request test: %s", err)
}
if !bytes.Equal(msg.Data, response) {
t.Fatalf("Received invalid response")
}
}
func TestRequest(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
})
msg, err := nc.Request("foo", []byte("help"), 500*time.Millisecond)
if err != nil {
t.Fatalf("Received an error on Request test: %s", err)
}
if !bytes.Equal(msg.Data, response) {
t.Fatalf("Received invalid response")
}
}
func TestRequestNoBody(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
})
msg, err := nc.Request("foo", nil, 500*time.Millisecond)
if err != nil {
t.Fatalf("Received an error on Request test: %s", err)
}
if !bytes.Equal(msg.Data, response) {
t.Fatalf("Received invalid response")
}
}
func TestSimultaneousRequests(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
})
var wg sync.WaitGroup
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
if _, err := nc.Request("foo", nil, 2*time.Second); err != nil {
t.Fatalf("Expected to receive a timeout error")
} else {
wg.Done()
}
}()
}
wg.Wait()
}
func TestRequestClose(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(100 * time.Millisecond)
nc.Close()
}()
if _, err := nc.Request("foo", []byte("help"), 2*time.Second); err != nats.ErrInvalidConnection && err != nats.ErrConnectionClosed {
t.Fatalf("Expected connection error: got %v", err)
}
wg.Wait()
}
func TestRequestCloseTimeout(t *testing.T) {
// Make sure we return a timeout when we close
// the connection even if response is queued.
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
response := []byte("I will help you")
nc.Subscribe("foo", func(m *nats.Msg) {
nc.Publish(m.Reply, response)
nc.Close()
})
if _, err := nc.Request("foo", nil, 1*time.Second); err == nil {
t.Fatalf("Expected to receive a timeout error")
}
}
func TestFlushInCB(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ch := make(chan bool)
nc.Subscribe("foo", func(_ *nats.Msg) {
nc.Flush()
ch <- true
})
nc.Publish("foo", []byte("Hello"))
if e := Wait(ch); e != nil {
t.Fatal("Flush did not return properly in callback")
}
}
func TestReleaseFlush(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
for i := 0; i < 1000; i++ {
nc.Publish("foo", []byte("Hello"))
}
go nc.Close()
nc.Flush()
}
func TestInbox(t *testing.T) {
inbox := nats.NewInbox()
if matched, _ := regexp.Match(`_INBOX.\S`, []byte(inbox)); !matched {
t.Fatal("Bad INBOX format")
}
}
func TestStats(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
data := []byte("The quick brown fox jumped over the lazy dog")
iter := 10
for i := 0; i < iter; i++ {
nc.Publish("foo", data)
}
if nc.OutMsgs != uint64(iter) {
t.Fatalf("Not properly tracking OutMsgs: received %d, wanted %d\n", nc.OutMsgs, iter)
}
obb := uint64(iter * len(data))
if nc.OutBytes != obb {
t.Fatalf("Not properly tracking OutBytes: received %d, wanted %d\n", nc.OutBytes, obb)
}
// Clear outbound
nc.OutMsgs, nc.OutBytes = 0, 0
// Test both sync and async versions of subscribe.
nc.Subscribe("foo", func(_ *nats.Msg) {})
nc.SubscribeSync("foo")
for i := 0; i < iter; i++ {
nc.Publish("foo", data)
}
nc.Flush()
if nc.InMsgs != uint64(2*iter) {
t.Fatalf("Not properly tracking InMsgs: received %d, wanted %d\n", nc.InMsgs, 2*iter)
}
ibb := 2 * obb
if nc.InBytes != ibb {
t.Fatalf("Not properly tracking InBytes: received %d, wanted %d\n", nc.InBytes, ibb)
}
}
func TestRaceSafeStats(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
go nc.Publish("foo", []byte("Hello World"))
time.Sleep(200 * time.Millisecond)
stats := nc.Stats()
if stats.OutMsgs != uint64(1) {
t.Fatalf("Not properly tracking OutMsgs: received %d, wanted %d\n", nc.OutMsgs, 1)
}
}
func TestBadSubject(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
err := nc.Publish("", []byte("Hello World"))
if err == nil {
t.Fatalf("Expected an error on bad subject to publish")
}
if err != nats.ErrBadSubject {
t.Fatalf("Expected a ErrBadSubject error: Got %v\n", err)
}
}
func TestOptions(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.Name("myName"), nats.MaxReconnects(2), nats.ReconnectWait(50*time.Millisecond))
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
rch := make(chan bool)
cch := make(chan bool)
nc.SetReconnectHandler(func(_ *nats.Conn) { rch <- true })
nc.SetClosedHandler(func(_ *nats.Conn) { cch <- true })
s.Shutdown()
s = RunDefaultServer()
defer s.Shutdown()
if err := Wait(rch); err != nil {
t.Fatal("Failed getting reconnected cb")
}
nc.Close()
if err := Wait(cch); err != nil {
t.Fatal("Failed getting closed cb")
}
nc, err = nats.Connect(nats.DefaultURL, nats.NoReconnect())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
nc.SetReconnectHandler(func(_ *nats.Conn) { rch <- true })
nc.SetClosedHandler(func(_ *nats.Conn) { cch <- true })
s.Shutdown()
// We should not get a reconnect cb this time
if err := WaitTime(rch, time.Second); err == nil {
t.Fatal("Unexpected reconnect cb")
}
nc.Close()
if err := Wait(cch); err != nil {
t.Fatal("Failed getting closed cb")
}
}
func TestNilConnection(t *testing.T) {
var nc *nats.Conn
data := []byte("ok")
// Publish
if err := nc.Publish("foo", data); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if err := nc.PublishMsg(nil); err == nil || err != nats.ErrInvalidMsg {
t.Fatalf("Expected ErrInvalidMsg error, got %v\n", err)
}
if err := nc.PublishMsg(&nats.Msg{}); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if err := nc.PublishRequest("foo", "reply", data); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
// Subscribe
if _, err := nc.Subscribe("foo", nil); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if _, err := nc.SubscribeSync("foo"); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if _, err := nc.QueueSubscribe("foo", "bar", nil); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
ch := make(chan *nats.Msg)
if _, err := nc.ChanSubscribe("foo", ch); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if _, err := nc.ChanQueueSubscribe("foo", "bar", ch); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if _, err := nc.QueueSubscribeSyncWithChan("foo", "bar", ch); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
// Flush
if err := nc.Flush(); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
if err := nc.FlushTimeout(time.Millisecond); err == nil || err != nats.ErrInvalidConnection {
t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err)
}
// Nil Subscribers
var sub *nats.Subscription
if sub.Type() != nats.NilSubscription {
t.Fatalf("Got wrong type for nil subscription, %v\n", sub.Type())
}
if sub.IsValid() {
t.Fatalf("Expected IsValid() to return false")
}
if err := sub.Unsubscribe(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected Unsubscribe to return proper error, got %v\n", err)
}
if err := sub.AutoUnsubscribe(1); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, err := sub.NextMsg(time.Millisecond); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, err := sub.QueuedMsgs(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, _, err := sub.Pending(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, _, err := sub.MaxPending(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if err := sub.ClearMaxPending(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, _, err := sub.PendingLimits(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if err := sub.SetPendingLimits(1, 1); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, err := sub.Delivered(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
if _, err := sub.Dropped(); err == nil || err != nats.ErrBadSubscription {
t.Fatalf("Expected ErrBadSubscription error, got %v\n", err)
}
}

View File

@ -0,0 +1,153 @@
package test
import (
"sync/atomic"
"testing"
"time"
"github.com/nats-io/go-nats"
)
func BenchmarkPublishSpeed(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
b.StartTimer()
msg := []byte("Hello World")
for i := 0; i < b.N; i++ {
if err := nc.Publish("foo", msg); err != nil {
b.Fatalf("Error in benchmark during Publish: %v\n", err)
}
}
// Make sure they are all processed.
nc.Flush()
b.StopTimer()
}
func BenchmarkPubSubSpeed(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
ch := make(chan bool)
nc.SetErrorHandler(func(nc *nats.Conn, s *nats.Subscription, err error) {
b.Fatalf("Error : %v\n", err)
})
received := int32(0)
nc.Subscribe("foo", func(m *nats.Msg) {
if nr := atomic.AddInt32(&received, 1); nr >= int32(b.N) {
ch <- true
}
})
msg := []byte("Hello World")
b.StartTimer()
for i := 0; i < b.N; i++ {
if err := nc.Publish("foo", msg); err != nil {
b.Fatalf("Error in benchmark during Publish: %v\n", err)
}
// Don't overrun ourselves and be a slow consumer, server will cut us off
if int32(i)-atomic.LoadInt32(&received) > 32768 {
time.Sleep(100 * time.Nanosecond)
}
}
// Make sure they are all processed.
err := WaitTime(ch, 10*time.Second)
if err != nil {
b.Fatal("Timed out waiting for messages")
} else if atomic.LoadInt32(&received) != int32(b.N) {
b.Fatalf("Received: %d, err:%v", received, nc.LastError())
}
b.StopTimer()
}
func BenchmarkAsyncSubscriptionCreationSpeed(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
b.StartTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
nc.Subscribe("foo", func(m *nats.Msg) {})
}
}
func BenchmarkSyncSubscriptionCreationSpeed(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
b.StartTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
nc.SubscribeSync("foo")
}
}
func BenchmarkInboxCreation(b *testing.B) {
for i := 0; i < b.N; i++ {
nats.NewInbox()
}
}
func BenchmarkRequest(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(b)
defer nc.Close()
ok := []byte("ok")
nc.Subscribe("req", func(m *nats.Msg) {
nc.Publish(m.Reply, ok)
})
b.StartTimer()
b.ReportAllocs()
q := []byte("q")
for i := 0; i < b.N; i++ {
_, err := nc.Request("req", q, 1*time.Second)
if err != nil {
b.Fatalf("Err %v\n", err)
}
}
}
func BenchmarkOldRequest(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
b.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
ok := []byte("ok")
nc.Subscribe("req", func(m *nats.Msg) {
nc.Publish(m.Reply, ok)
})
b.StartTimer()
b.ReportAllocs()
q := []byte("q")
for i := 0; i < b.N; i++ {
_, err := nc.Request("req", q, 1*time.Second)
if err != nil {
b.Fatalf("Err %v\n", err)
}
}
}

View File

@ -0,0 +1,606 @@
package test
import (
"math"
"regexp"
"runtime"
"strings"
"sync"
"testing"
"time"
"github.com/nats-io/gnatsd/test"
"github.com/nats-io/go-nats"
)
var testServers = []string{
"nats://localhost:1222",
"nats://localhost:1223",
"nats://localhost:1224",
"nats://localhost:1225",
"nats://localhost:1226",
"nats://localhost:1227",
"nats://localhost:1228",
}
var servers = strings.Join(testServers, ",")
func TestServersOption(t *testing.T) {
opts := nats.GetDefaultOptions()
opts.NoRandomize = true
_, err := opts.Connect()
if err != nats.ErrNoServers {
t.Fatalf("Wrong error: '%v'\n", err)
}
opts.Servers = testServers
_, err = opts.Connect()
if err == nil || err != nats.ErrNoServers {
t.Fatalf("Did not receive proper error: %v\n", err)
}
// Make sure we can connect to first server if running
s1 := RunServerOnPort(1222)
// Do this in case some failure occurs before explicit shutdown
defer s1.Shutdown()
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Could not connect: %v\n", err)
}
if nc.ConnectedUrl() != "nats://localhost:1222" {
nc.Close()
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
nc.Close()
s1.Shutdown()
// Make sure we can connect to a non first server if running
s2 := RunServerOnPort(1223)
// Do this in case some failure occurs before explicit shutdown
defer s2.Shutdown()
nc, err = opts.Connect()
if err != nil {
t.Fatalf("Could not connect: %v\n", err)
}
defer nc.Close()
if nc.ConnectedUrl() != "nats://localhost:1223" {
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
}
func TestNewStyleServersOption(t *testing.T) {
_, err := nats.Connect(nats.DefaultURL, nats.DontRandomize())
if err != nats.ErrNoServers {
t.Fatalf("Wrong error: '%v'\n", err)
}
servers := strings.Join(testServers, ",")
_, err = nats.Connect(servers, nats.DontRandomize())
if err == nil || err != nats.ErrNoServers {
t.Fatalf("Did not receive proper error: %v\n", err)
}
// Make sure we can connect to first server if running
s1 := RunServerOnPort(1222)
// Do this in case some failure occurs before explicit shutdown
defer s1.Shutdown()
nc, err := nats.Connect(servers, nats.DontRandomize())
if err != nil {
t.Fatalf("Could not connect: %v\n", err)
}
if nc.ConnectedUrl() != "nats://localhost:1222" {
nc.Close()
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
nc.Close()
s1.Shutdown()
// Make sure we can connect to a non-first server if running
s2 := RunServerOnPort(1223)
// Do this in case some failure occurs before explicit shutdown
defer s2.Shutdown()
nc, err = nats.Connect(servers, nats.DontRandomize())
if err != nil {
t.Fatalf("Could not connect: %v\n", err)
}
defer nc.Close()
if nc.ConnectedUrl() != "nats://localhost:1223" {
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
}
func TestAuthServers(t *testing.T) {
var plainServers = []string{
"nats://localhost:1222",
"nats://localhost:1224",
}
opts := test.DefaultTestOptions
opts.Username = "derek"
opts.Password = "foo"
opts.Port = 1222
as1 := RunServerWithOptions(opts)
defer as1.Shutdown()
opts.Port = 1224
as2 := RunServerWithOptions(opts)
defer as2.Shutdown()
pservers := strings.Join(plainServers, ",")
nc, err := nats.Connect(pservers, nats.DontRandomize(), nats.Timeout(5*time.Second))
if err == nil {
nc.Close()
t.Fatalf("Expect Auth failure, got no error\n")
}
if matched, _ := regexp.Match(`authorization`, []byte(err.Error())); !matched {
t.Fatalf("Wrong error, wanted Auth failure, got '%s'\n", err)
}
// Test that we can connect to a subsequent correct server.
var authServers = []string{
"nats://localhost:1222",
"nats://derek:foo@localhost:1224",
}
aservers := strings.Join(authServers, ",")
nc, err = nats.Connect(aservers, nats.DontRandomize(), nats.Timeout(5*time.Second))
if err != nil {
t.Fatalf("Expected to connect properly: %v\n", err)
}
defer nc.Close()
if nc.ConnectedUrl() != authServers[1] {
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
}
func TestBasicClusterReconnect(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
s2 := RunServerOnPort(1224)
defer s2.Shutdown()
dch := make(chan bool)
rch := make(chan bool)
dcbCalled := false
opts := []nats.Option{nats.DontRandomize(),
nats.DisconnectHandler(func(nc *nats.Conn) {
// Suppress any additional callbacks
if dcbCalled {
return
}
dcbCalled = true
dch <- true
}),
nats.ReconnectHandler(func(_ *nats.Conn) { rch <- true }),
}
nc, err := nats.Connect(servers, opts...)
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// wait for disconnect
if e := WaitTime(dch, 2*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
reconnectTimeStart := time.Now()
// wait for reconnect
if e := WaitTime(rch, 2*time.Second); e != nil {
t.Fatal("Did not receive a reconnect callback message")
}
if nc.ConnectedUrl() != testServers[2] {
t.Fatalf("Does not report correct connection: %s\n",
nc.ConnectedUrl())
}
// Make sure we did not wait on reconnect for default time.
// Reconnect should be fast since it will be a switch to the
// second server and not be dependent on server restart time.
// On Windows, a failed connect takes more than a second, so
// account for that.
maxDuration := 100 * time.Millisecond
if runtime.GOOS == "windows" {
maxDuration = 1100 * time.Millisecond
}
reconnectTime := time.Since(reconnectTimeStart)
if reconnectTime > maxDuration {
t.Fatalf("Took longer than expected to reconnect: %v\n", reconnectTime)
}
}
func TestHotSpotReconnect(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
var srvrs string
if runtime.GOOS == "windows" {
srvrs = strings.Join(testServers[:5], ",")
} else {
srvrs = servers
}
numClients := 32
clients := []*nats.Conn{}
wg := &sync.WaitGroup{}
wg.Add(numClients)
opts := []nats.Option{
nats.ReconnectWait(50 * time.Millisecond),
nats.ReconnectHandler(func(_ *nats.Conn) { wg.Done() }),
}
for i := 0; i < numClients; i++ {
nc, err := nats.Connect(srvrs, opts...)
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
if nc.ConnectedUrl() != testServers[0] {
t.Fatalf("Connected to incorrect server: %v\n", nc.ConnectedUrl())
}
clients = append(clients, nc)
}
s2 := RunServerOnPort(1224)
defer s2.Shutdown()
s3 := RunServerOnPort(1226)
defer s3.Shutdown()
s1.Shutdown()
numServers := 2
// Wait on all reconnects
wg.Wait()
// Walk the clients and calculate how many of each..
cs := make(map[string]int)
for _, nc := range clients {
cs[nc.ConnectedUrl()]++
nc.Close()
}
if len(cs) != numServers {
t.Fatalf("Wrong number of reported servers: %d vs %d\n", len(cs), numServers)
}
expected := numClients / numServers
v := uint(float32(expected) * 0.40)
// Check that each item is within acceptable range
for s, total := range cs {
delta := uint(math.Abs(float64(expected - total)))
if delta > v {
t.Fatalf("Connected clients to server: %s out of range: %d\n", s, total)
}
}
}
func TestProperReconnectDelay(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
var srvs string
opts := nats.GetDefaultOptions()
if runtime.GOOS == "windows" {
srvs = strings.Join(testServers[:2], ",")
} else {
srvs = strings.Join(testServers, ",")
}
opts.NoRandomize = true
dcbCalled := false
closedCbCalled := false
dch := make(chan bool)
dcb := func(nc *nats.Conn) {
// Suppress any additional calls
if dcbCalled {
return
}
dcbCalled = true
dch <- true
}
ccb := func(_ *nats.Conn) {
closedCbCalled = true
}
nc, err := nats.Connect(srvs, nats.DontRandomize(), nats.DisconnectHandler(dcb), nats.ClosedHandler(ccb))
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// wait for disconnect
if e := WaitTime(dch, 2*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
// Wait, want to make sure we don't spin on reconnect to non-existent servers.
time.Sleep(1 * time.Second)
// Make sure we are still reconnecting..
if closedCbCalled {
t.Fatal("Closed CB was triggered, should not have been.")
}
if status := nc.Status(); status != nats.RECONNECTING {
t.Fatalf("Wrong status: %d\n", status)
}
}
func TestProperFalloutAfterMaxAttempts(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
opts := nats.GetDefaultOptions()
// Reduce the list of servers for Windows tests
if runtime.GOOS == "windows" {
opts.Servers = testServers[:2]
opts.MaxReconnect = 2
} else {
opts.Servers = testServers
opts.MaxReconnect = 5
}
opts.NoRandomize = true
opts.ReconnectWait = (25 * time.Millisecond)
dch := make(chan bool)
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
closedCbCalled := false
cch := make(chan bool)
opts.ClosedCB = func(_ *nats.Conn) {
closedCbCalled = true
cch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// On Windows, creating a TCP connection to a server not running takes more than
// a second. So be generous with the WaitTime.
// wait for disconnect
if e := WaitTime(dch, 5*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
// Wait for ClosedCB
if e := WaitTime(cch, 5*time.Second); e != nil {
t.Fatal("Did not receive a closed callback message")
}
// Make sure we are not still reconnecting..
if !closedCbCalled {
t.Logf("%+v\n", nc)
t.Fatal("Closed CB was not triggered, should have been.")
}
// Expect connection to be closed...
if !nc.IsClosed() {
t.Fatalf("Wrong status: %d\n", nc.Status())
}
}
func TestProperFalloutAfterMaxAttemptsWithAuthMismatch(t *testing.T) {
var myServers = []string{
"nats://localhost:1222",
"nats://localhost:4443",
}
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
s2, _ := RunServerWithConfig("./configs/tlsverify.conf")
defer s2.Shutdown()
opts := nats.GetDefaultOptions()
opts.Servers = myServers
opts.NoRandomize = true
if runtime.GOOS == "windows" {
opts.MaxReconnect = 2
} else {
opts.MaxReconnect = 5
}
opts.ReconnectWait = (25 * time.Millisecond)
dch := make(chan bool)
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
closedCbCalled := false
cch := make(chan bool)
opts.ClosedCB = func(_ *nats.Conn) {
closedCbCalled = true
cch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// On Windows, creating a TCP connection to a server not running takes more than
// a second. So be generous with the WaitTime.
// wait for disconnect
if e := WaitTime(dch, 5*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
// Wait for ClosedCB
if e := WaitTime(cch, 5*time.Second); e != nil {
reconnects := nc.Stats().Reconnects
t.Fatalf("Did not receive a closed callback message, #reconnects: %v", reconnects)
}
// Make sure we have not exceeded MaxReconnect
reconnects := nc.Stats().Reconnects
if reconnects != uint64(opts.MaxReconnect) {
t.Fatalf("Num reconnects was %v, expected %v", reconnects, opts.MaxReconnect)
}
// Make sure we are not still reconnecting..
if !closedCbCalled {
t.Logf("%+v\n", nc)
t.Fatal("Closed CB was not triggered, should have been.")
}
// Expect connection to be closed...
if !nc.IsClosed() {
t.Fatalf("Wrong status: %d\n", nc.Status())
}
}
func TestTimeoutOnNoServers(t *testing.T) {
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
opts := nats.GetDefaultOptions()
if runtime.GOOS == "windows" {
opts.Servers = testServers[:2]
opts.MaxReconnect = 2
opts.ReconnectWait = (100 * time.Millisecond)
} else {
opts.Servers = testServers
// 1 second total time wait
opts.MaxReconnect = 10
opts.ReconnectWait = (100 * time.Millisecond)
}
opts.NoRandomize = true
dch := make(chan bool)
opts.DisconnectedCB = func(nc *nats.Conn) {
// Suppress any additional calls
nc.SetDisconnectHandler(nil)
dch <- true
}
cch := make(chan bool)
opts.ClosedCB = func(_ *nats.Conn) {
cch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
s1.Shutdown()
// On Windows, creating a connection to a non-running server takes
// more than a second. So be generous with WaitTime
// wait for disconnect
if e := WaitTime(dch, 5*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
startWait := time.Now()
// Wait for ClosedCB
if e := WaitTime(cch, 5*time.Second); e != nil {
t.Fatal("Did not receive a closed callback message")
}
if runtime.GOOS != "windows" {
timeWait := time.Since(startWait)
// Use 500ms as variable time delta
variable := (500 * time.Millisecond)
expected := (time.Duration(opts.MaxReconnect) * opts.ReconnectWait)
if timeWait > (expected + variable) {
t.Fatalf("Waited too long for Closed state: %d\n", timeWait/time.Millisecond)
}
}
}
func TestPingReconnect(t *testing.T) {
RECONNECTS := 4
s1 := RunServerOnPort(1222)
defer s1.Shutdown()
opts := nats.GetDefaultOptions()
opts.Servers = testServers
opts.NoRandomize = true
opts.ReconnectWait = 200 * time.Millisecond
opts.PingInterval = 50 * time.Millisecond
opts.MaxPingsOut = -1
var wg sync.WaitGroup
wg.Add(1)
rch := make(chan time.Time, RECONNECTS)
dch := make(chan time.Time, RECONNECTS)
opts.DisconnectedCB = func(_ *nats.Conn) {
d := dch
select {
case d <- time.Now():
default:
d = nil
}
}
opts.ReconnectedCB = func(c *nats.Conn) {
r := rch
select {
case r <- time.Now():
default:
r = nil
wg.Done()
}
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Expected to connect, got err: %v\n", err)
}
defer nc.Close()
wg.Wait()
s1.Shutdown()
<-dch
for i := 0; i < RECONNECTS-1; i++ {
disconnectedAt := <-dch
reconnectAt := <-rch
pingCycle := disconnectedAt.Sub(reconnectAt)
if pingCycle > 2*opts.PingInterval {
t.Fatalf("Reconnect due to ping took %s", pingCycle.String())
}
}
}

View File

@ -0,0 +1,38 @@
-----BEGIN CERTIFICATE-----
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
+Svp
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFPDCCAySgAwIBAgIJAO+k4G7bNTypMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR
BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv
Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy
MzEwNDdaFw0xOTExMDQyMzEwNDdaMBYxFDASBgNVBAMTC25hdHMtY2xpZW50MIIC
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArgLxszD5/vDrDUwwIEgQx9I0
J/H6MXPO0Tj9D2BnR+nwjCe9M03fsq4Il96BVzoaAiAQD1r4NyAX2adKydlnE3/m
bUFiSVHErJceEi9aSs+WlLdmKEgU2qrsIal9KzthlI786qtjb7OFSCxP14R4xYA5
dlZXhJ9oUuFhVTdaVmRMzWuWj8RbBx8VptSZ0f7Q+Uv8GuB0kyiVkv6GYcH/IWuI
7jnM0QcVWBmxJfWmqd0yx/FLlX/LRXqdiyoFSIlMaP0VOwto3uEhAoBk83Z+/zrZ
Brymx1Nnz3qzTCf8/mdMjPuWibXDTLbo0/Kf6neHs6wxx8irb1ZfIwhn8grXTcgd
rg9bfcyyUOBey7QXiedpU0xFqoH26E+Aq+CV4R56i1sJKsSYEGu8O69H8zu5dgan
LZRhcCHcZhMe7Nbiu5BcuOW4r3rGDMTLXSugEX91iy5jJaYmRjtPN5imQIJtf+GK
Vq7YLv4MQV6R3xRiZXaocCae1qzIMc4kxCKvZTmxuJsvIUPjNnGumwbjV/a2fLFX
9tMqUKyEmiPtFtqNH/kmkHCQ5FGYIIj3wGuD5yWfK5Tr3iHOdNJoNNPgPBg9tMRw
j3+W8+uyBxc+FUEb8a9m3R4VmAYyiqgzCA0DWZBF1fOYLWfRnwS5OBKiP4OUlUEb
YZUEzfvDbLOwQrb123cCAwEAAaMXMBUwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJ
KoZIhvcNAQELBQADggIBACNKPbvaXwl5rRTqFw37Am1r6e+LkUg9dFogSwXDnuT/
RRZJi5MHsC5MUOkHB28lTmPwkAogs+LBmKrM0Npzk6OPkT/LCgKqpVoz2Tc1nGMI
Jy8jxPYogMmDCOhoEoC7zsWABMLiX5KDAuKommk61w7AwKu4kK198ngwbfF2fzdH
1DUGID7iV4fyPGI+pCU3Ullv51c5xkhqjVy1JYdYc0+s6rFyVTibSABa7PfHE2ML
A+cNFWoKQhugVHQU7qYvuWvnEqZro2T6nmSmpK3oOaUgVnDuY2q4JwiMbZAtuyD7
8LFwCim49WzgYcfs/BwKlUrTV/QBYurruHWjElZzwA39/ZlbnOjJJ85j/YqxR+4S
fK/KktegyrPJU3fxdl2+77zVlfgzxaQ//58vx5LgXWhl2KeHyakeD0jQFVn1R7GD
bynAlHlSOr+nGkwP2WVqXKf+l/gb/gUEY7bC8fCVRCctkcK+smEl+sIKH3O9JY8l
rBWjOXkMY91ZDh77hfTNni/s2/DGAoNrEft8rgu3/NPxhCTfQH3ranCryth9mF6I
qsOFr5/81WGKqU+Kec8st/RSU2vBjBp41HILAEEhUiB6prhc9B3+exwkvQSPz22W
PIvhkzqeOYRoEDE2bWGC1ukd818qvQp618eLBmJSvwGh4YfUcmgqHaEk2NjoPIMV
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgIJAO+k4G7bNTyoMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR
BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv
Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy
MzA2MzRaFw0xOTExMDQyMzA2MzRaMBQxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALYBy3IEY3kqlf5h2vEtk9CB4Vnt
AD+eaVc3Y9xuFHJ4k0ScsjwrYH3YcwQW0fDpOQCI0102YoQT7tPCBT+rC0w1mM82
0ZSKS/y2SIK9cM6LHWkUbQcWfeaL+uz2HB3jTEm8tmEEFTLBJFMbMpUsvjA5GLqG
URswsNjYEl8M9wS1BETw2e+eCFa4wxq9oGHp/Dgh0vZglHzQL5osEpRct1aaQo6O
jWzZc1Cgx4SxmMOoMWF8BQzlO7aikbZEJxk03TIFNph/azJt7mviMseW72mP+bX9
sm/8bsINiYgJMM2HAyjIgFVMXX8AYfEFC4wozYloLqn0yy9TdjhyGbsUjg0yTd4l
A9LkGKroBdY1drPSek5Nj9br27UGGGfU2ddAD5xYBIeeFY+3nqST868oIXB/m1P7
1p8tpkgujx/RqKr3nvOqBHizmoQaWZsPC3X/Jc4NvVHihpuNzN/u1D5mxGhxsx+R
qnrIkhS0IqNrokggPZazugmHntd95HgTb3JpjY3RGEYXAQNr+mZGUCc+CVu0mhFX
xAMZcfVp5nDg4hKHiaRv0KcaqBmnn8AB5w5FiTppzUbRP0zz7GkwrdulwR6c2Eb5
75+/022TbgCx8B9SH4zJRTj5mtrK56eFgTcnuXB+YnWaP7/7qmKIZzxrd3UDvnza
bhnMiiIK7vL8qiOTAgMBAAGjHjAcMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA
ATANBgkqhkiG9w0BAQsFAAOCAgEAOrh8XfW6quwBAcCxHf6/uvu/iNq4yHCg2qH6
VtWs/x38I2t3BRSNsLsJGieh6yLlZDzOus+XYui4uDE50XmcwaIsY0VcXnvdyZVZ
w9+lMyfp00kRF1o3B6eVxq0pRE5VB0cai7XI7tyfpRwGzA+oNLF4vBvxAHm9Ony5
Q57DC/HFzyUogdkMYciO/kd9oa4HosDEXwaE8UvZUL8OVl/dptMXLL/GGwzZsUAE
1sLAbgm044YChLUDzgBAtDTkB/HNkcPzSKwULuskhe7ndoaEQNXVZuP7quGiZ/W1
1lE59gnmnyG8ySFCL05jHrKLtFAJe88gQjgDK65ZJv4W/k7ocmT+HhCxWyQWcX6v
abJ0EssqeSQuzRMuZebMJJ8s46d6RcYuMdIX3RDXq+1moJDFopE7lgNrlRhWgaky
Og8f/u8s1j75tk1YaYcY9uBKjKk7f681R9wMumkd6IEmEvkUwHNFsctxi4fGI7h1
PRdKL0DlhVmnpHlKs6Kvm2sJ3twSAGSrC4u0LuxACeR3XbiBfyhFV/291LSuw/y1
JtWOW5koh0g1k9xtkiu3/ePVdG/CLp796IyRhdB1jP/vD7W5RLLG/VAlomfjsPsB
AnwFYbVZ8KrmMKYUpTJOH31CRzFdOB6nWqXu5tk3nOtLKo1nIOuVtmp9XLz3VtHe
NiZPnqA=
-----END CERTIFICATE-----

View File

@ -0,0 +1,17 @@
# Simple TLS config file
port: 4443
net: localhost # net interface
tls {
cert_file: "./configs/certs/server.pem"
key_file: "./configs/certs/key.pem"
timeout: 2
}
authorization {
user: derek
password: buckley
timeout: 1
}

View File

@ -0,0 +1,17 @@
# Simple TLS config file
port: 4443
net: localhost
tls {
cert_file: "./configs/certs/server.pem"
key_file: "./configs/certs/key.pem"
timeout: 2
# Optional certificate authority for clients
ca_file: "./configs/certs/ca.pem"
# Require a client certificate
verify: true
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,999 @@
// Copyright 2012-2017 Apcera Inc. All rights reserved.
// +build go1.7
package test
import (
"context"
"strings"
"testing"
"time"
"github.com/nats-io/go-nats"
)
func TestContextRequestWithNilConnection(t *testing.T) {
var nc *nats.Conn
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
_, err := nc.RequestWithContext(ctx, "fast", []byte(""))
if err == nil {
t.Fatalf("Expected request with context and nil connection to fail\n")
}
if err != nats.ErrInvalidConnection {
t.Fatalf("Expected nats.ErrInvalidConnection, got %v\n", err)
}
}
func testContextRequestWithTimeout(t *testing.T, nc *nats.Conn) {
nc.Subscribe("slow", func(m *nats.Msg) {
// Simulates latency into the client so that timeout is hit.
time.Sleep(200 * time.Millisecond)
nc.Publish(m.Reply, []byte("NG"))
})
nc.Subscribe("fast", func(m *nats.Msg) {
nc.Publish(m.Reply, []byte("OK"))
})
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
// Fast request should not fail at this point.
resp, err := nc.RequestWithContext(ctx, "fast", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail on fast response: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
// Slow request hits timeout so expected to fail.
_, err = nc.RequestWithContext(ctx, "slow", []byte("world"))
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected = `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
// 2nd request should fail again even if they would be fast because context
// has already timed out.
_, err = nc.RequestWithContext(ctx, "fast", []byte("world"))
if err == nil {
t.Fatalf("Expected request with context to fail: %s", err)
}
}
func TestContextRequestWithTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
testContextRequestWithTimeout(t, nc)
}
func TestOldContextRequestWithTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
testContextRequestWithTimeout(t, nc)
}
func testContextRequestWithTimeoutCanceled(t *testing.T, nc *nats.Conn) {
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB()
nc.Subscribe("fast", func(m *nats.Msg) {
nc.Publish(m.Reply, []byte("OK"))
})
// Fast request should not fail
resp, err := nc.RequestWithContext(ctx, "fast", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail on fast response: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
// Cancel the context already so that rest of requests fail.
cancelCB()
_, err = nc.RequestWithContext(ctx, "fast", []byte("world"))
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
// Reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected = `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
// 2nd request should fail again even if fast because context has already been canceled
_, err = nc.RequestWithContext(ctx, "fast", []byte("world"))
if err == nil {
t.Fatalf("Expected request with context to fail: %s", err)
}
}
func TestContextRequestWithTimeoutCanceled(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
testContextRequestWithTimeoutCanceled(t, nc)
}
func TestOldContextRequestWithTimeoutCanceled(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
testContextRequestWithTimeoutCanceled(t, nc)
}
func testContextRequestWithCancel(t *testing.T, nc *nats.Conn) {
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB() // should always be called, not discarded, to prevent context leak
// timer which cancels the context though can also be arbitrarily extended
expirationTimer := time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
nc.Subscribe("slow", func(m *nats.Msg) {
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
nc.Publish(m.Reply, []byte("OK"))
})
nc.Subscribe("slower", func(m *nats.Msg) {
// we know this request will take longer so extend the timeout
expirationTimer.Reset(100 * time.Millisecond)
// slower reply which would have hit original timeout
time.Sleep(90 * time.Millisecond)
nc.Publish(m.Reply, []byte("Also OK"))
})
for i := 0; i < 2; i++ {
resp, err := nc.RequestWithContext(ctx, "slow", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// A third request with latency would make the context
// get canceled, but these reset the timer so deadline
// gets extended:
for i := 0; i < 10; i++ {
resp, err := nc.RequestWithContext(ctx, "slower", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "Also OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// One more slow request will expire the timer and cause an error...
_, err := nc.RequestWithContext(ctx, "slow", []byte(""))
if err == nil {
t.Fatalf("Expected request with cancellation context to fail: %s", err)
}
// ...though reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected := `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextRequestWithCancel(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
testContextRequestWithCancel(t, nc)
}
func TestOldContextRequestWithCancel(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
testContextRequestWithCancel(t, nc)
}
func testContextRequestWithDeadline(t *testing.T, nc *nats.Conn) {
deadline := time.Now().Add(100 * time.Millisecond)
ctx, cancelCB := context.WithDeadline(context.Background(), deadline)
defer cancelCB() // should always be called, not discarded, to prevent context leak
nc.Subscribe("slow", func(m *nats.Msg) {
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
nc.Publish(m.Reply, []byte("OK"))
})
for i := 0; i < 2; i++ {
resp, err := nc.RequestWithContext(ctx, "slow", []byte(""))
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// A third request with latency would make the context
// reach the deadline.
_, err := nc.RequestWithContext(ctx, "slow", []byte(""))
if err == nil {
t.Fatalf("Expected request with context to reach deadline: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextRequestWithDeadline(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
testContextRequestWithDeadline(t, nc)
}
func TestOldContextRequestWithDeadline(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer nc.Close()
testContextRequestWithDeadline(t, nc)
}
func TestContextSubNextMsgWithTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
sub, err := nc.SubscribeSync("slow")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
for i := 0; i < 2; i++ {
err := nc.Publish("slow", []byte("OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
// Enough time to get a couple of messages
time.Sleep(40 * time.Millisecond)
msg, err := sub.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected to receive message: %s", err)
}
got := string(msg.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// Third message will fail because the context will be canceled by now
_, err = sub.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected to fail receiving a message: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextSubNextMsgWithTimeoutCanceled(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
sub, err := nc.SubscribeSync("fast")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
for i := 0; i < 2; i++ {
err := nc.Publish("fast", []byte("OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
// Enough time to get a couple of messages
time.Sleep(40 * time.Millisecond)
msg, err := sub.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected to receive message: %s", err)
}
got := string(msg.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// Cancel the context already so that rest of NextMsg calls fail.
cancelCB()
_, err = sub.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
// Reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected := `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextSubNextMsgWithCancel(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB() // should always be called, not discarded, to prevent context leak
// timer which cancels the context though can also be arbitrarily extended
time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
sub1, err := nc.SubscribeSync("foo")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
sub2, err := nc.SubscribeSync("bar")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
for i := 0; i < 2; i++ {
err := nc.Publish("foo", []byte("OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
resp, err := sub1.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
err = nc.Publish("bar", []byte("Also OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
resp, err := sub2.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := string(resp.Data)
expected := "Also OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
// We do not have another message pending so timer will
// cancel the context.
_, err = sub2.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected request with context to fail: %s", err)
}
// Reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected = `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextSubNextMsgWithDeadline(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
deadline := time.Now().Add(100 * time.Millisecond)
ctx, cancelCB := context.WithDeadline(context.Background(), deadline)
defer cancelCB() // should always be called, not discarded, to prevent context leak
sub, err := nc.SubscribeSync("slow")
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
for i := 0; i < 2; i++ {
err := nc.Publish("slow", []byte("OK"))
if err != nil {
t.Fatalf("Expected publish to not fail: %s", err)
}
// Enough time to get a couple of messages
time.Sleep(40 * time.Millisecond)
msg, err := sub.NextMsgWithContext(ctx)
if err != nil {
t.Fatalf("Expected to receive message: %s", err)
}
got := string(msg.Data)
expected := "OK"
if got != expected {
t.Errorf("Expected to receive %s, got: %s", expected, got)
}
}
// Third message will fail because the context will be canceled by now
_, err = sub.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected to fail receiving a message: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextEncodedRequestWithTimeout(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
deadline := time.Now().Add(100 * time.Millisecond)
ctx, cancelCB := context.WithDeadline(context.Background(), deadline)
defer cancelCB() // should always be called, not discarded, to prevent context leak
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
c.Subscribe("slow", func(_, reply string, req *request) {
got := req.Message
expected := "Hello"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
for i := 0; i < 2; i++ {
req := &request{Message: "Hello"}
resp := &response{}
err := c.RequestWithContext(ctx, "slow", req, resp)
if err != nil {
t.Fatalf("Expected encoded request with context to not fail: %s", err)
}
got := resp.Code
expected := 200
if got != expected {
t.Errorf("Expected to receive %v, got: %v", expected, got)
}
}
// A third request with latency would make the context
// reach the deadline.
req := &request{Message: "Hello"}
resp := &response{}
err = c.RequestWithContext(ctx, "slow", req, resp)
if err == nil {
t.Fatalf("Expected request with context to reach deadline: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextEncodedRequestWithTimeoutCanceled(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelCB() // should always be called, not discarded, to prevent context leak
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
c.Subscribe("fast", func(_, reply string, req *request) {
got := req.Message
expected := "Hello"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
// Fast request should not fail
req := &request{Message: "Hello"}
resp := &response{}
c.RequestWithContext(ctx, "fast", req, resp)
expectedCode := 200
if resp.Code != expectedCode {
t.Errorf("Expected to receive %d, got: %d", expectedCode, resp.Code)
}
// Cancel the context already so that rest of requests fail.
cancelCB()
err = c.RequestWithContext(ctx, "fast", req, resp)
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
// Reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected := `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
// 2nd request should fail again even if fast because context has already been canceled
err = c.RequestWithContext(ctx, "fast", req, resp)
if err == nil {
t.Fatalf("Expected request with timeout context to fail: %s", err)
}
}
func TestContextEncodedRequestWithCancel(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB() // should always be called, not discarded, to prevent context leak
// timer which cancels the context though can also be arbitrarily extended
expirationTimer := time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
c.Subscribe("slow", func(_, reply string, req *request) {
got := req.Message
expected := "Hello"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
c.Subscribe("slower", func(_, reply string, req *request) {
got := req.Message
expected := "World"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// we know this request will take longer so extend the timeout
expirationTimer.Reset(100 * time.Millisecond)
// slower reply which would have hit original timeout
time.Sleep(90 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
for i := 0; i < 2; i++ {
req := &request{Message: "Hello"}
resp := &response{}
err := c.RequestWithContext(ctx, "slow", req, resp)
if err != nil {
t.Fatalf("Expected encoded request with context to not fail: %s", err)
}
got := resp.Code
expected := 200
if got != expected {
t.Errorf("Expected to receive %v, got: %v", expected, got)
}
}
// A third request with latency would make the context
// get canceled, but these reset the timer so deadline
// gets extended:
for i := 0; i < 10; i++ {
req := &request{Message: "World"}
resp := &response{}
err := c.RequestWithContext(ctx, "slower", req, resp)
if err != nil {
t.Fatalf("Expected request with context to not fail: %s", err)
}
got := resp.Code
expected := 200
if got != expected {
t.Errorf("Expected to receive %d, got: %d", expected, got)
}
}
req := &request{Message: "Hello"}
resp := &response{}
// One more slow request will expire the timer and cause an error...
err = c.RequestWithContext(ctx, "slow", req, resp)
if err == nil {
t.Fatalf("Expected request with cancellation context to fail: %s", err)
}
// ...though reported error is "context canceled" from Context package,
// which is not a timeout error.
type timeoutError interface {
Timeout() bool
}
if _, ok := err.(timeoutError); ok {
t.Errorf("Expected to not have a timeout error")
}
expected := `context canceled`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextEncodedRequestWithDeadline(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
deadline := time.Now().Add(100 * time.Millisecond)
ctx, cancelCB := context.WithDeadline(context.Background(), deadline)
defer cancelCB() // should always be called, not discarded, to prevent context leak
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
c.Subscribe("slow", func(_, reply string, req *request) {
got := req.Message
expected := "Hello"
if got != expected {
t.Errorf("Expected to receive request with %q, got %q", got, expected)
}
// simulates latency into the client so that timeout is hit.
time.Sleep(40 * time.Millisecond)
c.Publish(reply, &response{Code: 200})
})
for i := 0; i < 2; i++ {
req := &request{Message: "Hello"}
resp := &response{}
err := c.RequestWithContext(ctx, "slow", req, resp)
if err != nil {
t.Fatalf("Expected encoded request with context to not fail: %s", err)
}
got := resp.Code
expected := 200
if got != expected {
t.Errorf("Expected to receive %v, got: %v", expected, got)
}
}
// A third request with latency would make the context
// reach the deadline.
req := &request{Message: "Hello"}
resp := &response{}
err = c.RequestWithContext(ctx, "slow", req, resp)
if err == nil {
t.Fatalf("Expected request with context to reach deadline: %s", err)
}
// Reported error is "context deadline exceeded" from Context package,
// which implements net.Error Timeout interface.
type timeoutError interface {
Timeout() bool
}
timeoutErr, ok := err.(timeoutError)
if !ok || !timeoutErr.Timeout() {
t.Errorf("Expected to have a timeout error")
}
expected := `context deadline exceeded`
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected %q error, got: %q", expected, err.Error())
}
}
func TestContextRequestConnClosed(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB()
time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
nc.Close()
_, err := nc.RequestWithContext(ctx, "foo", []byte(""))
if err == nil {
t.Fatalf("Expected request to fail with error")
}
if err != nats.ErrConnectionClosed {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
}
func TestContextBadSubscription(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
defer nc.Close()
ctx, cancelCB := context.WithCancel(context.Background())
defer cancelCB()
time.AfterFunc(100*time.Millisecond, func() {
cancelCB()
})
sub, err := nc.Subscribe("foo", func(_ *nats.Msg) {})
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
err = sub.Unsubscribe()
if err != nil {
t.Fatalf("Expected to be able to unsubscribe: %s", err)
}
_, err = sub.NextMsgWithContext(ctx)
if err == nil {
t.Fatalf("Expected to fail getting next message with context")
}
if err != nats.ErrBadSubscription {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
}
func TestContextInvalid(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
nc := NewDefaultConnection(t)
c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Unable to create encoded connection: %v", err)
}
defer c.Close()
_, err = nc.RequestWithContext(nil, "foo", []byte(""))
if err == nil {
t.Fatalf("Expected request to fail with error")
}
if err != nats.ErrInvalidContext {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
sub, err := nc.Subscribe("foo", func(_ *nats.Msg) {})
if err != nil {
t.Fatalf("Expected to be able to subscribe: %s", err)
}
_, err = sub.NextMsgWithContext(nil)
if err == nil {
t.Fatalf("Expected request to fail with error")
}
if err != nats.ErrInvalidContext {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
type request struct {
Message string `json:"message"`
}
type response struct {
Code int `json:"code"`
}
req := &request{Message: "Hello"}
resp := &response{}
err = c.RequestWithContext(nil, "slow", req, resp)
if err == nil {
t.Fatalf("Expected request with context to reach deadline: %s", err)
}
if err != nats.ErrInvalidContext {
t.Errorf("Expected request to fail with connection closed error: %s", err)
}
}

View File

@ -0,0 +1,448 @@
// Copyright 2012-2017 Apcera Inc. All rights reserved.
package test
import (
"bytes"
"testing"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/go-nats/encoders/builtin"
)
const TEST_PORT = 8168
func NewDefaultEConn(t *testing.T) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewConnection(t, TEST_PORT), nats.DEFAULT_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
func TestEncBuiltinConstructorErrs(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
c := NewConnection(t, TEST_PORT)
_, err := nats.NewEncodedConn(nil, "default")
if err == nil {
t.Fatal("Expected err for nil connection")
}
_, err = nats.NewEncodedConn(c, "foo22")
if err == nil {
t.Fatal("Expected err for bad encoder")
}
c.Close()
_, err = nats.NewEncodedConn(c, "default")
if err == nil {
t.Fatal("Expected err for closed connection")
}
}
func TestEncBuiltinMarshalString(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
ec.Subscribe("enc_string", func(s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
ch <- true
})
ec.Publish("enc_string", testString)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalBytes(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testBytes := []byte("Hello World!")
ec.Subscribe("enc_bytes", func(b []byte) {
if !bytes.Equal(b, testBytes) {
t.Fatalf("Received test bytes of '%s', wanted '%s'\n", b, testBytes)
}
ch <- true
})
ec.Publish("enc_bytes", testBytes)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalInt(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("enc_int", func(n int) {
if n != testN {
t.Fatalf("Received test number of %d, wanted %d\n", n, testN)
}
ch <- true
})
ec.Publish("enc_int", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalInt32(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("enc_int", func(n int32) {
if n != int32(testN) {
t.Fatalf("Received test number of %d, wanted %d\n", n, testN)
}
ch <- true
})
ec.Publish("enc_int", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalInt64(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("enc_int", func(n int64) {
if n != int64(testN) {
t.Fatalf("Received test number of %d, wanted %d\n", n, testN)
}
ch <- true
})
ec.Publish("enc_int", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalFloat32(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := float32(22)
ec.Subscribe("enc_float", func(n float32) {
if n != testN {
t.Fatalf("Received test number of %f, wanted %f\n", n, testN)
}
ch <- true
})
ec.Publish("enc_float", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalFloat64(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testN := float64(22.22)
ec.Subscribe("enc_float", func(n float64) {
if n != testN {
t.Fatalf("Received test number of %f, wanted %f\n", n, testN)
}
ch <- true
})
ec.Publish("enc_float", testN)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinMarshalBool(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
expected := make(chan bool, 1)
ec.Subscribe("enc_bool", func(b bool) {
val := <-expected
if b != val {
t.Fatal("Boolean values did not match")
}
ch <- true
})
expected <- false
ec.Publish("enc_bool", false)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
expected <- true
ec.Publish("enc_bool", true)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinExtendedSubscribeCB(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
subject := "cb_args"
ec.Subscribe(subject, func(subj, s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
if subj != subject {
t.Fatalf("Received subject of '%s', wanted '%s'\n", subj, subject)
}
ch <- true
})
ec.Publish(subject, testString)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinExtendedSubscribeCB2(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
oSubj := "cb_args"
oReply := "foobar"
ec.Subscribe(oSubj, func(subj, reply, s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
if subj != oSubj {
t.Fatalf("Received subject of '%s', wanted '%s'\n", subj, oSubj)
}
if reply != oReply {
t.Fatalf("Received reply of '%s', wanted '%s'\n", reply, oReply)
}
ch <- true
})
ec.PublishRequest(oSubj, oReply, testString)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinRawMsgSubscribeCB(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
oSubj := "cb_args"
oReply := "foobar"
ec.Subscribe(oSubj, func(m *nats.Msg) {
s := string(m.Data)
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
if m.Subject != oSubj {
t.Fatalf("Received subject of '%s', wanted '%s'\n", m.Subject, oSubj)
}
if m.Reply != oReply {
t.Fatalf("Received reply of '%s', wanted '%s'\n", m.Reply, oReply)
}
ch <- true
})
ec.PublishRequest(oSubj, oReply, testString)
if e := Wait(ch); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinRequest(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
expectedResp := "I can help!"
ec.Subscribe("help", func(subj, reply, req string) {
ec.Publish(reply, expectedResp)
})
var resp string
err := ec.Request("help", "help me", &resp, 1*time.Second)
if err != nil {
t.Fatalf("Failed at receiving proper response: %v\n", err)
}
if resp != expectedResp {
t.Fatalf("Received reply '%s', wanted '%s'\n", resp, expectedResp)
}
}
func TestEncBuiltinRequestReceivesMsg(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
expectedResp := "I can help!"
ec.Subscribe("help", func(subj, reply, req string) {
ec.Publish(reply, expectedResp)
})
var resp nats.Msg
err := ec.Request("help", "help me", &resp, 1*time.Second)
if err != nil {
t.Fatalf("Failed at receiving proper response: %v\n", err)
}
if string(resp.Data) != expectedResp {
t.Fatalf("Received reply '%s', wanted '%s'\n", string(resp.Data), expectedResp)
}
}
func TestEncBuiltinAsyncMarshalErr(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewDefaultEConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
subject := "err_marshall"
ec.Subscribe(subject, func(subj, num int) {
// This will never get called.
})
ec.Conn.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, err error) {
ch <- true
}
ec.Publish(subject, testString)
if e := Wait(ch); e != nil {
t.Fatalf("Did not receive the message: %s", e)
}
}
func TestEncBuiltinEncodeNil(t *testing.T) {
de := &builtin.DefaultEncoder{}
_, err := de.Encode("foo", nil)
if err != nil {
t.Fatalf("Expected no error encoding nil: %v", err)
}
}
func TestEncBuiltinDecodeDefault(t *testing.T) {
de := &builtin.DefaultEncoder{}
b, err := de.Encode("foo", 22)
if err != nil {
t.Fatalf("Expected no error encoding number: %v", err)
}
var c chan bool
err = de.Decode("foo", b, &c)
if err == nil {
t.Fatalf("Expected an error decoding")
}
}

View File

@ -0,0 +1,128 @@
// Copyright 2012-2017 Apcera Inc. All rights reserved.
package test
import (
"reflect"
"testing"
"github.com/nats-io/go-nats"
)
func NewGobEncodedConn(tl TestLogger) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewConnection(tl, TEST_PORT), nats.GOB_ENCODER)
if err != nil {
tl.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
func TestEncBuiltinGobMarshalString(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewGobEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
ec.Subscribe("gob_string", func(s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
ch <- true
})
ec.Publish("gob_string", testString)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func TestEncBuiltinGobMarshalInt(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewGobEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("gob_int", func(n int) {
if n != testN {
t.Fatalf("Received test int of '%d', wanted '%d'\n", n, testN)
}
ch <- true
})
ec.Publish("gob_int", testN)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func TestEncBuiltinGobMarshalStruct(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewGobEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
me.Assets = make(map[string]uint)
me.Assets["house"] = 1000
me.Assets["car"] = 100
ec.Subscribe("gob_struct", func(p *person) {
if !reflect.DeepEqual(p, me) {
t.Fatalf("Did not receive the correct struct response")
}
ch <- true
})
ec.Publish("gob_struct", me)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func BenchmarkPublishGobStruct(b *testing.B) {
// stop benchmark for set-up
b.StopTimer()
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewGobEncodedConn(b)
defer ec.Close()
ch := make(chan bool)
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
ec.Subscribe("gob_struct", func(p *person) {
if !reflect.DeepEqual(p, me) {
b.Fatalf("Did not receive the correct struct response")
}
ch <- true
})
// resume benchmark
b.StartTimer()
for n := 0; n < b.N; n++ {
ec.Publish("gob_struct", me)
if e := Wait(ch); e != nil {
b.Fatal("Did not receive the message")
}
}
}

View File

@ -0,0 +1,93 @@
// Copyright 2015 Apcera Inc. All rights reserved.
package test
import (
"errors"
"fmt"
"time"
"github.com/nats-io/gnatsd/server"
"github.com/nats-io/go-nats"
gnatsd "github.com/nats-io/gnatsd/test"
)
// So that we can pass tests and benchmarks...
type tLogger interface {
Fatalf(format string, args ...interface{})
Errorf(format string, args ...interface{})
}
// TestLogger
type TestLogger tLogger
// Dumb wait program to sync on callbacks, etc... Will timeout
func Wait(ch chan bool) error {
return WaitTime(ch, 5*time.Second)
}
// Wait for a chan with a timeout.
func WaitTime(ch chan bool, timeout time.Duration) error {
select {
case <-ch:
return nil
case <-time.After(timeout):
}
return errors.New("timeout")
}
////////////////////////////////////////////////////////////////////////////////
// Creating client connections
////////////////////////////////////////////////////////////////////////////////
// NewDefaultConnection
func NewDefaultConnection(t tLogger) *nats.Conn {
return NewConnection(t, nats.DefaultPort)
}
// NewConnection forms connection on a given port.
func NewConnection(t tLogger, port int) *nats.Conn {
url := fmt.Sprintf("nats://localhost:%d", port)
nc, err := nats.Connect(url)
if err != nil {
t.Fatalf("Failed to create default connection: %v\n", err)
return nil
}
return nc
}
// NewEConn
func NewEConn(t tLogger) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewDefaultConnection(t), nats.DEFAULT_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
////////////////////////////////////////////////////////////////////////////////
// Running gnatsd server in separate Go routines
////////////////////////////////////////////////////////////////////////////////
// RunDefaultServer will run a server on the default port.
func RunDefaultServer() *server.Server {
return RunServerOnPort(nats.DefaultPort)
}
// RunServerOnPort will run a server on the given port.
func RunServerOnPort(port int) *server.Server {
opts := gnatsd.DefaultTestOptions
opts.Port = port
return RunServerWithOptions(opts)
}
// RunServerWithOptions will run a server with the given options.
func RunServerWithOptions(opts server.Options) *server.Server {
return gnatsd.RunServer(&opts)
}
// RunServerWithConfig will run a server with the given configuration file.
func RunServerWithConfig(configFile string) (*server.Server, *server.Options) {
return gnatsd.RunServerWithConfig(configFile)
}

View File

@ -0,0 +1,209 @@
// Copyright 2012-2017 Apcera Inc. All rights reserved.
package test
import (
"reflect"
"testing"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/go-nats/encoders/builtin"
)
func NewJsonEncodedConn(tl TestLogger) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewConnection(tl, TEST_PORT), nats.JSON_ENCODER)
if err != nil {
tl.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
func TestEncBuiltinJsonMarshalString(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
testString := "Hello World!"
ec.Subscribe("json_string", func(s string) {
if s != testString {
t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString)
}
ch <- true
})
ec.Publish("json_string", testString)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func TestEncBuiltinJsonMarshalInt(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
testN := 22
ec.Subscribe("json_int", func(n int) {
if n != testN {
t.Fatalf("Received test int of '%d', wanted '%d'\n", n, testN)
}
ch <- true
})
ec.Publish("json_int", testN)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
type person struct {
Name string
Address string
Age int
Children map[string]*person
Assets map[string]uint
}
func TestEncBuiltinJsonMarshalStruct(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
me.Assets = make(map[string]uint)
me.Assets["house"] = 1000
me.Assets["car"] = 100
ec.Subscribe("json_struct", func(p *person) {
if !reflect.DeepEqual(p, me) {
t.Fatal("Did not receive the correct struct response")
}
ch <- true
})
ec.Publish("json_struct", me)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func BenchmarkJsonMarshalStruct(b *testing.B) {
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
encoder := &builtin.JsonEncoder{}
for n := 0; n < b.N; n++ {
if _, err := encoder.Encode("protobuf_test", me); err != nil {
b.Fatal("Couldn't serialize object", err)
}
}
}
func BenchmarkPublishJsonStruct(b *testing.B) {
// stop benchmark for set-up
b.StopTimer()
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(b)
defer ec.Close()
ch := make(chan bool)
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*person)
me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
ec.Subscribe("json_struct", func(p *person) {
if !reflect.DeepEqual(p, me) {
b.Fatalf("Did not receive the correct struct response")
}
ch <- true
})
// resume benchmark
b.StartTimer()
for n := 0; n < b.N; n++ {
ec.Publish("json_struct", me)
if e := Wait(ch); e != nil {
b.Fatal("Did not receive the message")
}
}
}
func TestEncBuiltinNotMarshableToJson(t *testing.T) {
je := &builtin.JsonEncoder{}
ch := make(chan bool)
_, err := je.Encode("foo", ch)
if err == nil {
t.Fatal("Expected an error when failing encoding")
}
}
func TestEncBuiltinFailedEncodedPublish(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewJsonEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
err := ec.Publish("foo", ch)
if err == nil {
t.Fatal("Expected an error trying to publish a channel")
}
err = ec.PublishRequest("foo", "bar", ch)
if err == nil {
t.Fatal("Expected an error trying to publish a channel")
}
var cr chan bool
err = ec.Request("foo", ch, &cr, 1*time.Second)
if err == nil {
t.Fatal("Expected an error trying to publish a channel")
}
err = ec.LastError()
if err != nil {
t.Fatalf("Expected LastError to be nil: %q ", err)
}
}
func TestEncBuiltinDecodeConditionals(t *testing.T) {
je := &builtin.JsonEncoder{}
b, err := je.Encode("foo", 22)
if err != nil {
t.Fatalf("Expected no error when encoding, got %v\n", err)
}
var foo string
var bar []byte
err = je.Decode("foo", b, &foo)
if err != nil {
t.Fatalf("Expected no error when decoding, got %v\n", err)
}
err = je.Decode("foo", b, &bar)
if err != nil {
t.Fatalf("Expected no error when decoding, got %v\n", err)
}
}

14
gateway/vendor/github.com/nats-io/go-nats/test/main.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
// Copyright 2017 Apcera Inc. All rights reserved.
package test
import (
"os"
"testing"
)
// TestMain runs all tests. Added since tests were moved to a separate package.
func TestMain(m *testing.M) {
// call flag.Parse() here if TestMain uses flags
os.Exit(m.Run())
}

View File

@ -0,0 +1,355 @@
package test
import (
"runtime"
"testing"
"time"
"github.com/nats-io/go-nats"
)
func TestBadChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
if err := ec.BindSendChan("foo", "not a chan"); err == nil {
t.Fatalf("Expected an Error when sending a non-channel\n")
}
if _, err := ec.BindRecvChan("foo", "not a chan"); err == nil {
t.Fatalf("Expected an Error when sending a non-channel\n")
}
if err := ec.BindSendChan("foo", "not a chan"); err != nats.ErrChanArg {
t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
}
if _, err := ec.BindRecvChan("foo", "not a chan"); err != nats.ErrChanArg {
t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
}
}
func TestSimpleSendChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
recv := make(chan bool)
numSent := int32(22)
ch := make(chan int32)
if err := ec.BindSendChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
ec.Subscribe("foo", func(num int32) {
if num != numSent {
t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
}
recv <- true
})
// Send to 'foo'
ch <- numSent
if e := Wait(recv); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
close(ch)
}
func TestFailedChannelSend(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
nc := ec.Conn
ch := make(chan bool)
wch := make(chan bool)
nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
wch <- true
}
if err := ec.BindSendChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a receive channel: %v\n", err)
}
nc.Flush()
go func() {
time.Sleep(100 * time.Millisecond)
nc.Close()
}()
func() {
for {
select {
case ch <- true:
case <-wch:
return
case <-time.After(time.Second):
t.Fatal("Failed to get async error cb")
}
}
}()
ec = NewEConn(t)
defer ec.Close()
nc = ec.Conn
bch := make(chan []byte)
nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
wch <- true
}
if err := ec.BindSendChan("foo", bch); err != nil {
t.Fatalf("Failed to bind to a receive channel: %v\n", err)
}
buf := make([]byte, 2*1024*1024)
bch <- buf
if e := Wait(wch); e != nil {
t.Fatal("Failed to call async err handler")
}
}
func TestSimpleRecvChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
numSent := int32(22)
ch := make(chan int32)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a receive channel: %v\n", err)
}
ec.Publish("foo", numSent)
// Receive from 'foo'
select {
case num := <-ch:
if num != numSent {
t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
}
case <-time.After(1 * time.Second):
t.Fatalf("Failed to receive a value, timed-out\n")
}
close(ch)
}
func TestQueueRecvChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
numSent := int32(22)
ch := make(chan int32)
if _, err := ec.BindRecvQueueChan("foo", "bar", ch); err != nil {
t.Fatalf("Failed to bind to a queue receive channel: %v\n", err)
}
ec.Publish("foo", numSent)
// Receive from 'foo'
select {
case num := <-ch:
if num != numSent {
t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
}
case <-time.After(1 * time.Second):
t.Fatalf("Failed to receive a value, timed-out\n")
}
close(ch)
}
func TestDecoderErrRecvChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
nc := ec.Conn
wch := make(chan bool)
nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
wch <- true
}
ch := make(chan *int32)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
ec.Publish("foo", "Hello World")
if e := Wait(wch); e != nil {
t.Fatal("Failed to call async err handler")
}
}
func TestRecvChanPanicOnClosedChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
ch := make(chan int)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
close(ch)
ec.Publish("foo", 22)
ec.Flush()
}
func TestRecvChanAsyncLeakGoRoutines(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
// Call this to make sure that we have everything setup connection wise
ec.Flush()
before := runtime.NumGoroutine()
ch := make(chan int)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
// Close the receive Channel
close(ch)
// The publish will trigger the close and shutdown of the Go routines
ec.Publish("foo", 22)
ec.Flush()
time.Sleep(100 * time.Millisecond)
delta := (runtime.NumGoroutine() - before)
if delta > 0 {
t.Fatalf("Leaked Go routine(s) : %d, closing channel should have closed them\n", delta)
}
}
func TestRecvChanLeakGoRoutines(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
// Call this to make sure that we have everything setup connection wise
ec.Flush()
before := runtime.NumGoroutine()
ch := make(chan int)
sub, err := ec.BindRecvChan("foo", ch)
if err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
sub.Unsubscribe()
// Sleep a bit to wait for the Go routine to exit.
time.Sleep(500 * time.Millisecond)
delta := (runtime.NumGoroutine() - before)
if delta > 0 {
t.Fatalf("Leaked Go routine(s) : %d, closing channel should have closed them\n", delta)
}
}
func TestRecvChanMultipleMessages(t *testing.T) {
// Make sure we can receive more than one message.
// In response to #25, which is a bug from fixing #22.
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
// Num to send, should == len of messages queued.
size := 10
ch := make(chan int, size)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
for i := 0; i < size; i++ {
ec.Publish("foo", 22)
}
ec.Flush()
time.Sleep(10 * time.Millisecond)
if lch := len(ch); lch != size {
t.Fatalf("Expected %d messages queued, got %d.", size, lch)
}
}
func BenchmarkPublishSpeedViaChan(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL)
if err != nil {
b.Fatalf("Could not connect: %v\n", err)
}
ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER)
if err != nil {
b.Fatalf("Failed creating encoded connection: %v\n", err)
}
defer ec.Close()
ch := make(chan int32, 1024)
if err := ec.BindSendChan("foo", ch); err != nil {
b.Fatalf("Failed to bind to a send channel: %v\n", err)
}
b.StartTimer()
num := int32(22)
for i := 0; i < b.N; i++ {
ch <- num
}
// Make sure they are all processed.
nc.Flush()
b.StopTimer()
}

View File

@ -0,0 +1,126 @@
package test
import (
"reflect"
"testing"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/go-nats/encoders/protobuf"
pb "github.com/nats-io/go-nats/encoders/protobuf/testdata"
)
func NewProtoEncodedConn(tl TestLogger) *nats.EncodedConn {
ec, err := nats.NewEncodedConn(NewConnection(tl, TEST_PORT), protobuf.PROTOBUF_ENCODER)
if err != nil {
tl.Fatalf("Failed to create an encoded connection: %v\n", err)
}
return ec
}
func TestEncProtoMarshalStruct(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewProtoEncodedConn(t)
defer ec.Close()
ch := make(chan bool)
me := &pb.Person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*pb.Person)
me.Children["sam"] = &pb.Person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &pb.Person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
ec.Subscribe("protobuf_test", func(p *pb.Person) {
if !reflect.DeepEqual(p, me) {
t.Fatal("Did not receive the correct protobuf response")
}
ch <- true
})
ec.Publish("protobuf_test", me)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive the message")
}
}
func TestEncProtoNilRequest(t *testing.T) {
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewProtoEncodedConn(t)
defer ec.Close()
testPerson := &pb.Person{Name: "Anatolii", Age: 25, Address: "Ukraine, Nikolaev"}
//Subscribe with empty interface shouldn't failed on empty message
ec.Subscribe("nil_test", func(_, reply string, _ interface{}) {
ec.Publish(reply, testPerson)
})
resp := new(pb.Person)
//Request with nil argument shouldn't failed with nil argument
err := ec.Request("nil_test", nil, resp, 100*time.Millisecond)
ec.Flush()
if err != nil {
t.Error("Fail to send empty message via encoded proto connection")
}
if !reflect.DeepEqual(testPerson, resp) {
t.Error("Fail to receive encoded response")
}
}
func BenchmarkProtobufMarshalStruct(b *testing.B) {
me := &pb.Person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*pb.Person)
me.Children["sam"] = &pb.Person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &pb.Person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
encoder := &protobuf.ProtobufEncoder{}
for n := 0; n < b.N; n++ {
if _, err := encoder.Encode("protobuf_test", me); err != nil {
b.Fatal("Couldn't serialize object", err)
}
}
}
func BenchmarkPublishProtobufStruct(b *testing.B) {
// stop benchmark for set-up
b.StopTimer()
s := RunServerOnPort(TEST_PORT)
defer s.Shutdown()
ec := NewProtoEncodedConn(b)
defer ec.Close()
ch := make(chan bool)
me := &pb.Person{Name: "derek", Age: 22, Address: "140 New Montgomery St"}
me.Children = make(map[string]*pb.Person)
me.Children["sam"] = &pb.Person{Name: "sam", Age: 19, Address: "140 New Montgomery St"}
me.Children["meg"] = &pb.Person{Name: "meg", Age: 17, Address: "140 New Montgomery St"}
ec.Subscribe("protobuf_test", func(p *pb.Person) {
if !reflect.DeepEqual(p, me) {
b.Fatalf("Did not receive the correct protobuf response")
}
ch <- true
})
// resume benchmark
b.StartTimer()
for n := 0; n < b.N; n++ {
ec.Publish("protobuf_test", me)
if e := Wait(ch); e != nil {
b.Fatal("Did not receive the message")
}
}
}

View File

@ -0,0 +1,623 @@
package test
import (
"sync"
"sync/atomic"
"testing"
"time"
"github.com/nats-io/gnatsd/server"
"github.com/nats-io/go-nats"
)
func startReconnectServer(t *testing.T) *server.Server {
return RunServerOnPort(22222)
}
func TestReconnectTotalTime(t *testing.T) {
opts := nats.GetDefaultOptions()
totalReconnectTime := time.Duration(opts.MaxReconnect) * opts.ReconnectWait
if totalReconnectTime < (2 * time.Minute) {
t.Fatalf("Total reconnect time should be at least 2 mins: Currently %v\n",
totalReconnectTime)
}
}
func TestReconnectDisallowedFlags(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
ch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Url = "nats://localhost:22222"
opts.AllowReconnect = false
opts.ClosedCB = func(_ *nats.Conn) {
ch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
ts.Shutdown()
if e := Wait(ch); e != nil {
t.Fatal("Did not trigger ClosedCB correctly")
}
}
func TestReconnectAllowedFlags(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
ch := make(chan bool)
dch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Url = "nats://localhost:22222"
opts.AllowReconnect = true
opts.MaxReconnect = 2
opts.ReconnectWait = 1 * time.Second
opts.ClosedCB = func(_ *nats.Conn) {
ch <- true
}
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
ts.Shutdown()
// We want wait to timeout here, and the connection
// should not trigger the Close CB.
if e := WaitTime(ch, 500*time.Millisecond); e == nil {
t.Fatal("Triggered ClosedCB incorrectly")
}
// We should wait to get the disconnected callback to ensure
// that we are in the process of reconnecting.
if e := Wait(dch); e != nil {
t.Fatal("DisconnectedCB should have been triggered")
}
if !nc.IsReconnecting() {
t.Fatal("Expected to be in a reconnecting state")
}
// clear the CloseCB since ch will block
nc.Opts.ClosedCB = nil
}
var reconnectOpts = nats.Options{
Url: "nats://localhost:22222",
AllowReconnect: true,
MaxReconnect: 10,
ReconnectWait: 100 * time.Millisecond,
Timeout: nats.DefaultTimeout,
}
func TestConnCloseBreaksReconnectLoop(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
cch := make(chan bool)
opts := reconnectOpts
// Bump the max reconnect attempts
opts.MaxReconnect = 100
opts.ClosedCB = func(_ *nats.Conn) {
cch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
nc.Flush()
// Shutdown the server
ts.Shutdown()
// Wait a second, then close the connection
time.Sleep(time.Second)
// Close the connection, this should break the reconnect loop.
// Do this in a go routine since the issue was that Close()
// would block until the reconnect loop is done.
go nc.Close()
// Even on Windows (where a createConn takes more than a second)
// we should be able to break the reconnect loop with the following
// timeout.
if err := WaitTime(cch, 3*time.Second); err != nil {
t.Fatal("Did not get a closed callback")
}
}
func TestBasicReconnectFunctionality(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
ch := make(chan bool)
dch := make(chan bool)
opts := reconnectOpts
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v\n", err)
}
defer nc.Close()
ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
testString := "bar"
ec.Subscribe("foo", func(s string) {
if s != testString {
t.Fatal("String doesn't match")
}
ch <- true
})
ec.Flush()
ts.Shutdown()
// server is stopped here...
if err := Wait(dch); err != nil {
t.Fatalf("Did not get the disconnected callback on time\n")
}
if err := ec.Publish("foo", testString); err != nil {
t.Fatalf("Failed to publish message: %v\n", err)
}
ts = startReconnectServer(t)
defer ts.Shutdown()
if err := ec.FlushTimeout(5 * time.Second); err != nil {
t.Fatalf("Error on Flush: %v", err)
}
if e := Wait(ch); e != nil {
t.Fatal("Did not receive our message")
}
expectedReconnectCount := uint64(1)
reconnectCount := ec.Conn.Stats().Reconnects
if reconnectCount != expectedReconnectCount {
t.Fatalf("Reconnect count incorrect: %d vs %d\n",
reconnectCount, expectedReconnectCount)
}
}
func TestExtendedReconnectFunctionality(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
opts := reconnectOpts
dch := make(chan bool)
opts.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
rch := make(chan bool)
opts.ReconnectedCB = func(_ *nats.Conn) {
rch <- true
}
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
testString := "bar"
received := int32(0)
ec.Subscribe("foo", func(s string) {
atomic.AddInt32(&received, 1)
})
sub, _ := ec.Subscribe("foobar", func(s string) {
atomic.AddInt32(&received, 1)
})
ec.Publish("foo", testString)
ec.Flush()
ts.Shutdown()
// server is stopped here..
// wait for disconnect
if e := WaitTime(dch, 2*time.Second); e != nil {
t.Fatal("Did not receive a disconnect callback message")
}
// Sub while disconnected
ec.Subscribe("bar", func(s string) {
atomic.AddInt32(&received, 1)
})
// Unsub foobar while disconnected
sub.Unsubscribe()
if err = ec.Publish("foo", testString); err != nil {
t.Fatalf("Received an error after disconnect: %v\n", err)
}
if err = ec.Publish("bar", testString); err != nil {
t.Fatalf("Received an error after disconnect: %v\n", err)
}
ts = startReconnectServer(t)
defer ts.Shutdown()
// server is restarted here..
// wait for reconnect
if e := WaitTime(rch, 2*time.Second); e != nil {
t.Fatal("Did not receive a reconnect callback message")
}
if err = ec.Publish("foobar", testString); err != nil {
t.Fatalf("Received an error after server restarted: %v\n", err)
}
if err = ec.Publish("foo", testString); err != nil {
t.Fatalf("Received an error after server restarted: %v\n", err)
}
ch := make(chan bool)
ec.Subscribe("done", func(b bool) {
ch <- true
})
ec.Publish("done", true)
if e := Wait(ch); e != nil {
t.Fatal("Did not receive our message")
}
// Sleep a bit to guarantee scheduler runs and process all subs.
time.Sleep(50 * time.Millisecond)
if atomic.LoadInt32(&received) != 4 {
t.Fatalf("Received != %d, equals %d\n", 4, received)
}
}
func TestQueueSubsOnReconnect(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
opts := reconnectOpts
// Allow us to block on reconnect complete.
reconnectsDone := make(chan bool)
opts.ReconnectedCB = func(nc *nats.Conn) {
reconnectsDone <- true
}
// Create connection
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v\n", err)
}
defer nc.Close()
ec, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
t.Fatalf("Failed to create an encoded connection: %v\n", err)
}
// To hold results.
results := make(map[int]int)
var mu sync.Mutex
// Make sure we got what we needed, 1 msg only and all seqnos accounted for..
checkResults := func(numSent int) {
mu.Lock()
defer mu.Unlock()
for i := 0; i < numSent; i++ {
if results[i] != 1 {
t.Fatalf("Received incorrect number of messages, [%d] for seq: %d\n", results[i], i)
}
}
// Auto reset results map
results = make(map[int]int)
}
subj := "foo.bar"
qgroup := "workers"
cb := func(seqno int) {
mu.Lock()
defer mu.Unlock()
results[seqno] = results[seqno] + 1
}
// Create Queue Subscribers
ec.QueueSubscribe(subj, qgroup, cb)
ec.QueueSubscribe(subj, qgroup, cb)
ec.Flush()
// Helper function to send messages and check results.
sendAndCheckMsgs := func(numToSend int) {
for i := 0; i < numToSend; i++ {
ec.Publish(subj, i)
}
// Wait for processing.
ec.Flush()
time.Sleep(50 * time.Millisecond)
// Check Results
checkResults(numToSend)
}
// Base Test
sendAndCheckMsgs(10)
// Stop and restart server
ts.Shutdown()
ts = startReconnectServer(t)
defer ts.Shutdown()
if err := Wait(reconnectsDone); err != nil {
t.Fatal("Did not get the ReconnectedCB!")
}
// Reconnect Base Test
sendAndCheckMsgs(10)
}
func TestIsClosed(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
nc := NewConnection(t, 22222)
defer nc.Close()
if nc.IsClosed() {
t.Fatalf("IsClosed returned true when the connection is still open.")
}
ts.Shutdown()
if nc.IsClosed() {
t.Fatalf("IsClosed returned true when the connection is still open.")
}
ts = startReconnectServer(t)
defer ts.Shutdown()
if nc.IsClosed() {
t.Fatalf("IsClosed returned true when the connection is still open.")
}
nc.Close()
if !nc.IsClosed() {
t.Fatalf("IsClosed returned false after Close() was called.")
}
}
func TestIsReconnectingAndStatus(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
disconnectedch := make(chan bool)
reconnectch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Url = "nats://localhost:22222"
opts.AllowReconnect = true
opts.MaxReconnect = 10000
opts.ReconnectWait = 100 * time.Millisecond
opts.DisconnectedCB = func(_ *nats.Conn) {
disconnectedch <- true
}
opts.ReconnectedCB = func(_ *nats.Conn) {
reconnectch <- true
}
// Connect, verify initial reconnecting state check, then stop the server
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
if nc.IsReconnecting() {
t.Fatalf("IsReconnecting returned true when the connection is still open.")
}
if status := nc.Status(); status != nats.CONNECTED {
t.Fatalf("Status returned %d when connected instead of CONNECTED", status)
}
ts.Shutdown()
// Wait until we get the disconnected callback
if e := Wait(disconnectedch); e != nil {
t.Fatalf("Disconnect callback wasn't triggered: %v", e)
}
if !nc.IsReconnecting() {
t.Fatalf("IsReconnecting returned false when the client is reconnecting.")
}
if status := nc.Status(); status != nats.RECONNECTING {
t.Fatalf("Status returned %d when reconnecting instead of CONNECTED", status)
}
ts = startReconnectServer(t)
defer ts.Shutdown()
// Wait until we get the reconnect callback
if e := Wait(reconnectch); e != nil {
t.Fatalf("Reconnect callback wasn't triggered: %v", e)
}
if nc.IsReconnecting() {
t.Fatalf("IsReconnecting returned true after the connection was reconnected.")
}
if status := nc.Status(); status != nats.CONNECTED {
t.Fatalf("Status returned %d when reconnected instead of CONNECTED", status)
}
// Close the connection, reconnecting should still be false
nc.Close()
if nc.IsReconnecting() {
t.Fatalf("IsReconnecting returned true after Close() was called.")
}
if status := nc.Status(); status != nats.CLOSED {
t.Fatalf("Status returned %d after Close() was called instead of CLOSED", status)
}
}
func TestFullFlushChanDuringReconnect(t *testing.T) {
ts := startReconnectServer(t)
defer ts.Shutdown()
reconnectch := make(chan bool)
opts := nats.GetDefaultOptions()
opts.Url = "nats://localhost:22222"
opts.AllowReconnect = true
opts.MaxReconnect = 10000
opts.ReconnectWait = 100 * time.Millisecond
opts.ReconnectedCB = func(_ *nats.Conn) {
reconnectch <- true
}
// Connect
nc, err := opts.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
// Channel used to make the go routine sending messages to stop.
stop := make(chan bool)
// While connected, publish as fast as we can
go func() {
for i := 0; ; i++ {
_ = nc.Publish("foo", []byte("hello"))
// Make sure we are sending at least flushChanSize (1024) messages
// before potentially pausing.
if i%2000 == 0 {
select {
case <-stop:
return
default:
time.Sleep(100 * time.Millisecond)
}
}
}
}()
// Send a bit...
time.Sleep(500 * time.Millisecond)
// Shut down the server
ts.Shutdown()
// Continue sending while we are disconnected
time.Sleep(time.Second)
// Restart the server
ts = startReconnectServer(t)
defer ts.Shutdown()
// Wait for the reconnect CB to be invoked (but not for too long)
if e := WaitTime(reconnectch, 5*time.Second); e != nil {
t.Fatalf("Reconnect callback wasn't triggered: %v", e)
}
}
func TestReconnectVerbose(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
o := nats.GetDefaultOptions()
o.Verbose = true
rch := make(chan bool)
o.ReconnectedCB = func(_ *nats.Conn) {
rch <- true
}
nc, err := o.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
err = nc.Flush()
if err != nil {
t.Fatalf("Error during flush: %v", err)
}
s.Shutdown()
s = RunDefaultServer()
defer s.Shutdown()
if e := Wait(rch); e != nil {
t.Fatal("Should have reconnected ok")
}
err = nc.Flush()
if err != nil {
t.Fatalf("Error during flush: %v", err)
}
}
func TestReconnectBufSize(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
o := nats.GetDefaultOptions()
o.ReconnectBufSize = 32 // 32 bytes
dch := make(chan bool)
o.DisconnectedCB = func(_ *nats.Conn) {
dch <- true
}
nc, err := o.Connect()
if err != nil {
t.Fatalf("Should have connected ok: %v", err)
}
defer nc.Close()
err = nc.Flush()
if err != nil {
t.Fatalf("Error during flush: %v", err)
}
// Force disconnected state.
s.Shutdown()
if e := Wait(dch); e != nil {
t.Fatal("DisconnectedCB should have been triggered")
}
msg := []byte("food") // 4 bytes paylaod, total proto is 16 bytes
// These should work, 2X16 = 32
if err := nc.Publish("foo", msg); err != nil {
t.Fatalf("Failed to publish message: %v\n", err)
}
if err := nc.Publish("foo", msg); err != nil {
t.Fatalf("Failed to publish message: %v\n", err)
}
// This should fail since we have exhausted the backing buffer.
if err := nc.Publish("foo", msg); err == nil {
t.Fatalf("Expected to fail to publish message: got no error\n")
}
nc.Buffered()
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
package nats
import (
"testing"
"time"
)
func TestTimerPool(t *testing.T) {
var tp timerPool
for i := 0; i < 10; i++ {
tm := tp.Get(time.Millisecond * 20)
select {
case <-tm.C:
t.Errorf("Timer already expired")
continue
default:
}
select {
case <-tm.C:
case <-time.After(time.Millisecond * 100):
t.Errorf("Timer didn't expire in time")
}
tp.Put(tm)
}
}

View File

@ -1,37 +1,15 @@
// Copyright 2016 Apcera Inc. All rights reserved.
// +build go1.7
// Copyright 2017 Apcera Inc. All rights reserved.
// +build go1.8
package util
import (
"crypto/tls"
)
import "crypto/tls"
// CloneTLSConfig returns a copy of c. Only the exported fields are copied.
// This is temporary, until this is provided by the language.
// https://go-review.googlesource.com/#/c/28075/
// CloneTLSConfig returns a copy of c.
func CloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
Renegotiation: c.Renegotiation,
if c == nil {
return &tls.Config{}
}
return c.Clone()
}

View File

@ -0,0 +1,37 @@
// Copyright 2016 Apcera Inc. All rights reserved.
// +build go1.7,!go1.8
package util
import (
"crypto/tls"
)
// CloneTLSConfig returns a copy of c. Only the exported fields are copied.
// This is temporary, until this is provided by the language.
// https://go-review.googlesource.com/#/c/28075/
func CloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
Renegotiation: c.Renegotiation,
}
}

View File

@ -1,35 +0,0 @@
// Copyright 2016 Apcera Inc. All rights reserved.
// +build go1.5,!go1.7
package util
import (
"crypto/tls"
)
// CloneTLSConfig returns a copy of c. Only the exported fields are copied.
// This is temporary, until this is provided by the language.
// https://go-review.googlesource.com/#/c/28075/
func CloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
}
}