diff --git a/README.md b/README.md
index d2c7e7c0..a92004db 100644
--- a/README.md
+++ b/README.md
@@ -125,16 +125,19 @@ To set a field when an object already exists:
Tile38 has support to search for objects and points that are within or intersects other objects. All object types can be searched including Polygons, MultiPolygons, GeometryCollections, etc.
+
#### Within
WITHIN searches a collection for objects that are fully contained inside a specified bounding area.
+
#### Intersects
INTERSECTS searches a collection for objects that intersect a specified bounding area.
+
#### Nearby
NEARBY searches a collection for objects that intersect a specified radius.
diff --git a/controller/endpoint/amqp.go b/controller/endpoint/amqp.go
new file mode 100644
index 00000000..290ed51f
--- /dev/null
+++ b/controller/endpoint/amqp.go
@@ -0,0 +1,136 @@
+package endpoint
+
+import (
+ "sync"
+ "time"
+
+ "fmt"
+ "github.com/streadway/amqp"
+)
+
+const (
+ AMQPExpiresAfter = time.Second * 30
+)
+
+type AMQPEndpointConn struct {
+ mu sync.Mutex
+ ep Endpoint
+ conn *amqp.Connection
+ channel *amqp.Channel
+ ex bool
+ t time.Time
+}
+
+func (conn *AMQPEndpointConn) Expired() bool {
+ conn.mu.Lock()
+ defer conn.mu.Unlock()
+ if !conn.ex {
+ if time.Now().Sub(conn.t) > kafkaExpiresAfter {
+ conn.ex = true
+ conn.close()
+ }
+ }
+ return conn.ex
+}
+
+func (conn *AMQPEndpointConn) close() {
+ if conn.conn != nil {
+ conn.conn.Close()
+ conn.conn = nil
+ conn.channel = nil
+ }
+}
+
+func (conn *AMQPEndpointConn) Send(msg string) error {
+ conn.mu.Lock()
+ defer conn.mu.Unlock()
+
+ if conn.ex {
+ return errExpired
+ }
+ conn.t = time.Now()
+
+ if conn.conn == nil {
+ prefix := "amqp://"
+ if conn.ep.AMQP.SSL {
+ prefix = "amqps://"
+ }
+
+ c, err := amqp.Dial(fmt.Sprintf("%s%s", prefix, conn.ep.AMQP.URI))
+
+ if err != nil {
+ return err
+ }
+
+ channel, err := c.Channel()
+ if err != nil {
+ return err
+ }
+
+ // Declare new exchange
+ if err := channel.ExchangeDeclare(
+ conn.ep.AMQP.QueueName,
+ "direct",
+ true,
+ false,
+ false,
+ false,
+ nil,
+ ); err != nil {
+ return err
+ }
+
+ // Create queue if queue don't exists
+ if _, err := channel.QueueDeclare(
+ conn.ep.AMQP.QueueName,
+ true,
+
+ false,
+ false,
+ false,
+ nil,
+ ); err != nil {
+ return err
+ }
+
+ // Binding exchange to queue
+ if err := channel.QueueBind(
+ conn.ep.AMQP.QueueName,
+ conn.ep.AMQP.RouteKey,
+ conn.ep.AMQP.QueueName,
+ false,
+ nil,
+ ); err != nil {
+ return err
+ }
+
+ conn.conn = c
+ conn.channel = channel
+ }
+
+ if err := conn.channel.Publish(
+ conn.ep.AMQP.QueueName,
+ conn.ep.AMQP.RouteKey,
+ false,
+ false,
+ amqp.Publishing{
+ Headers: amqp.Table{},
+ ContentType: "application/json",
+ ContentEncoding: "",
+ Body: []byte(msg),
+ DeliveryMode: amqp.Transient,
+ Priority: 0,
+ },
+ ); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func newAMQPEndpointConn(ep Endpoint) *AMQPEndpointConn {
+ return &AMQPEndpointConn{
+ ep: ep,
+ t: time.Now(),
+ }
+}
diff --git a/controller/endpoint/endpoint.go b/controller/endpoint/endpoint.go
index 8ec4a240..ef9ba8ea 100644
--- a/controller/endpoint/endpoint.go
+++ b/controller/endpoint/endpoint.go
@@ -21,6 +21,7 @@ const (
Redis = EndpointProtocol("redis") // Redis
Kafka = EndpointProtocol("kafka") // Kafka
MQTT = EndpointProtocol("mqtt") // MQTT
+ AMQP = EndpointProtocol("amqp") // AMQP
)
// Endpoint represents an endpoint.
@@ -49,6 +50,12 @@ type Endpoint struct {
Port int
QueueName string
}
+ AMQP struct {
+ URI string
+ SSL bool
+ QueueName string
+ RouteKey string
+ }
MQTT struct {
Host string
Port int
@@ -125,6 +132,8 @@ func (epc *EndpointManager) Send(endpoint, val string) error {
conn = newKafkaEndpointConn(ep)
case MQTT:
conn = newMQTTEndpointConn(ep)
+ case AMQP:
+ conn = newAMQPEndpointConn(ep)
}
epc.conns[endpoint] = conn
}
@@ -161,6 +170,10 @@ func parseEndpoint(s string) (Endpoint, error) {
endpoint.Protocol = Redis
case strings.HasPrefix(s, "kafka:"):
endpoint.Protocol = Kafka
+ case strings.HasPrefix(s, "amqp:"):
+ endpoint.Protocol = AMQP
+ case strings.HasPrefix(s, "amqps:"):
+ endpoint.Protocol = AMQP
case strings.HasPrefix(s, "mqtt:"):
endpoint.Protocol = MQTT
}
@@ -370,5 +383,60 @@ func parseEndpoint(s string) (Endpoint, error) {
}
}
+ // Basic AMQP connection strings in HOOKS interface
+ // amqp://guest:guest@localhost:5672//?params=value
+ //
+ // Default params are:
+ //
+ // Mandatory - false
+ // Immeditate - false
+ // Durable - true
+ // Routing-Key - tile38
+ //
+ // - "route" - [string] routing key
+ //
+ if endpoint.Protocol == AMQP {
+ // Bind connection information
+ endpoint.AMQP.URI = s
+
+ // Bind queue name
+ if len(sp) > 1 {
+ var err error
+ endpoint.AMQP.QueueName, err = url.QueryUnescape(sp[1])
+ if err != nil {
+ return endpoint, errors.New("invalid AMQP queue name")
+ }
+ }
+
+ // Parsing additional attributes
+ if len(sqp) > 1 {
+ m, err := url.ParseQuery(sqp[1])
+ if err != nil {
+ return endpoint, errors.New("invalid AMQP url")
+ }
+ for key, val := range m {
+ if len(val) == 0 {
+ continue
+ }
+ switch key {
+ case "route":
+ endpoint.AMQP.RouteKey = val[0]
+ }
+ }
+ }
+
+ if strings.HasPrefix(endpoint.Original, "amqps:") {
+ endpoint.AMQP.SSL = true
+ }
+
+ if endpoint.AMQP.QueueName == "" {
+ return endpoint, errors.New("missing AMQP queue name")
+ }
+
+ if endpoint.AMQP.RouteKey == "" {
+ endpoint.AMQP.RouteKey = "tile38"
+ }
+ }
+
return endpoint, nil
}
diff --git a/vendor/github.com/streadway/amqp/CONTRIBUTING.md b/vendor/github.com/streadway/amqp/CONTRIBUTING.md
new file mode 100644
index 00000000..c87f3d7e
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/CONTRIBUTING.md
@@ -0,0 +1,35 @@
+## Prequisites
+
+1. Go: [https://golang.org/dl/](https://golang.org/dl/)
+1. Golint `go get -u -v github.com/golang/lint/golint`
+
+## Contributing
+
+The workflow is pretty standard:
+
+1. Fork github.com/streadway/amqp
+1. Add the pre-commit hook: `ln -s ../../pre-commit .git/hooks/pre-commit`
+1. Create your feature branch (`git checkout -b my-new-feature`)
+1. Run integration tests (see below)
+1. **Implement tests**
+1. Implement fixs
+1. Commit your changes (`git commit -am 'Add some feature'`)
+1. Push to a branch (`git push -u origin my-new-feature`)
+1. Submit a pull request
+
+## Running Tests
+
+The test suite assumes that:
+
+ * A RabbitMQ node is running on localhost with all defaults: [https://www.rabbitmq.com/download.html](https://www.rabbitmq.com/download.html)
+ * `AMQP_URL` is exported to `amqp://guest:guest@127.0.0.1:5672/`
+
+### Integration Tests
+
+After starting a local RabbitMQ, run integration tests with the following:
+
+ env AMQP_URL=amqp://guest:guest@127.0.0.1:5672/ go test -v -cpu 2 -tags integration -race
+
+All integration tests should use the `integrationConnection(...)` test
+helpers defined in `integration_test.go` to setup the integration environment
+and logging.
diff --git a/vendor/github.com/streadway/amqp/LICENSE b/vendor/github.com/streadway/amqp/LICENSE
new file mode 100644
index 00000000..243c0ce7
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/streadway/amqp/README.md b/vendor/github.com/streadway/amqp/README.md
new file mode 100644
index 00000000..6ca06fc4
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/README.md
@@ -0,0 +1,91 @@
+# Go RabbitMQ Client Library
+
+This is an AMQP 0.9.1 client with RabbitMQ extensions in Go.
+
+## Project Maturity
+
+This project has been used in production systems for over 3 years. It is reasonably mature
+and feature complete, and as of November 2016 has [a team of maintainers](https://github.com/streadway/amqp/issues/215)
+instead of being a one man show.
+
+Future API changes are unlikely but possible. They will be discussed on [Github
+issues](https://github.com/streadway/amqp/issues) along with any bugs or
+enhancements.
+
+## Supported RabbitMQ Versions
+
+This project supports RabbitMQ versions starting with `2.0` but primarily tested
+against reasonably recent `3.x` releases. Some features and behaviours may be
+server version-specific.
+
+## Goals
+
+Provide a functional interface that closely represents the AMQP 0.9.1 model
+targeted to RabbitMQ as a server. This includes the minimum necessary to
+interact the semantics of the protocol.
+
+## Non-goals
+
+Things not intended to be supported.
+
+ * Auto reconnect and re-synchronization of client and server topologies.
+ * Reconnection would require understanding the error paths when the
+ topology cannot be declared on reconnect. This would require a new set
+ of types and code paths that are best suited at the call-site of this
+ package. AMQP has a dynamic topology that needs all peers to agree. If
+ this doesn't happen, the behavior is undefined. Instead of producing a
+ possible interface with undefined behavior, this package is designed to
+ be simple for the caller to implement the necessary connection-time
+ topology declaration so that reconnection is trivial and encapsulated in
+ the caller's application code.
+ * AMQP Protocol negotiation for forward or backward compatibility.
+ * 0.9.1 is stable and widely deployed. Versions 0.10 and 1.0 are divergent
+ specifications that change the semantics and wire format of the protocol.
+ We will accept patches for other protocol support but have no plans for
+ implementation ourselves.
+ * Anything other than PLAIN and EXTERNAL authentication mechanisms.
+ * Keeping the mechanisms interface modular makes it possible to extend
+ outside of this package. If other mechanisms prove to be popular, then
+ we would accept patches to include them in this package.
+
+## Usage
+
+See the 'examples' subdirectory for simple producers and consumers executables.
+If you have a use-case in mind which isn't well-represented by the examples,
+please file an issue.
+
+## Documentation
+
+Use [Godoc documentation](http://godoc.org/github.com/streadway/amqp) for
+reference and usage.
+
+[RabbitMQ tutorials in
+Go](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go) are also
+available.
+
+## Continuous Integration
+
+[![Build Status](https://secure.travis-ci.org/streadway/amqp.png)](http://travis-ci.org/streadway/amqp)
+
+## Contributing
+
+Pull requests are very much welcomed. Create your pull request on a non-master
+branch, make sure a test or example is included that covers your change and
+your commits represent coherent changes that include a reason for the change.
+
+To run the integration tests, make sure you have RabbitMQ running on any host,
+export the environment variable `AMQP_URL=amqp://host/` and run `go test -tags
+integration`. TravisCI will also run the integration tests.
+
+Thanks to the [community of contributors](https://github.com/streadway/amqp/graphs/contributors).
+
+## External packages
+
+ * [Google App Engine Dialer support](https://github.com/soundtrackyourbrand/gaeamqp)
+ * [RabbitMQ examples in Go](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go)
+
+## License
+
+BSD 2 clause - see LICENSE for more details.
+
+
diff --git a/vendor/github.com/streadway/amqp/_examples/pubsub/pubsub.go b/vendor/github.com/streadway/amqp/_examples/pubsub/pubsub.go
new file mode 100644
index 00000000..55ad32b6
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/_examples/pubsub/pubsub.go
@@ -0,0 +1,231 @@
+// Command pubsub is an example of a fanout exchange with dynamic reliable
+// membership, reading from stdin, writing to stdout.
+//
+// This example shows how to implement reconnect logic independent from a
+// publish/subscribe loop with bridges to application types.
+
+package main
+
+import (
+ "bufio"
+ "crypto/sha1"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/streadway/amqp"
+ "golang.org/x/net/context"
+)
+
+var url = flag.String("url", "amqp:///", "AMQP url for both the publisher and subscriber")
+
+// exchange binds the publishers to the subscribers
+const exchange = "pubsub"
+
+// message is the application type for a message. This can contain identity,
+// or a reference to the recevier chan for further demuxing.
+type message []byte
+
+// session composes an amqp.Connection with an amqp.Channel
+type session struct {
+ *amqp.Connection
+ *amqp.Channel
+}
+
+// Close tears the connection down, taking the channel with it.
+func (s session) Close() error {
+ if s.Connection == nil {
+ return nil
+ }
+ return s.Connection.Close()
+}
+
+// redial continually connects to the URL, exiting the program when no longer possible
+func redial(ctx context.Context, url string) chan chan session {
+ sessions := make(chan chan session)
+
+ go func() {
+ sess := make(chan session)
+ defer close(sessions)
+
+ for {
+ select {
+ case sessions <- sess:
+ case <-ctx.Done():
+ log.Println("shutting down session factory")
+ return
+ }
+
+ conn, err := amqp.Dial(url)
+ if err != nil {
+ log.Fatalf("cannot (re)dial: %v: %q", err, url)
+ }
+
+ ch, err := conn.Channel()
+ if err != nil {
+ log.Fatalf("cannot create channel: %v", err)
+ }
+
+ if err := ch.ExchangeDeclare(exchange, "fanout", false, true, false, false, nil); err != nil {
+ log.Fatalf("cannot declare fanout exchange: %v", err)
+ }
+
+ select {
+ case sess <- session{conn, ch}:
+ case <-ctx.Done():
+ log.Println("shutting down new session")
+ return
+ }
+ }
+ }()
+
+ return sessions
+}
+
+// publish publishes messages to a reconnecting session to a fanout exchange.
+// It receives from the application specific source of messages.
+func publish(sessions chan chan session, messages <-chan message) {
+ var (
+ running bool
+ reading = messages
+ pending = make(chan message, 1)
+ confirm = make(chan amqp.Confirmation, 1)
+ )
+
+ for session := range sessions {
+ pub := <-session
+
+ // publisher confirms for this channel/connection
+ if err := pub.Confirm(false); err != nil {
+ log.Printf("publisher confirms not supported")
+ close(confirm) // confirms not supported, simulate by always nacking
+ } else {
+ pub.NotifyPublish(confirm)
+ }
+
+ log.Printf("publishing...")
+
+ Publish:
+ for {
+ var body message
+ select {
+ case confirmed := <-confirm:
+ if !confirmed.Ack {
+ log.Printf("nack message %d, body: %q", confirmed.DeliveryTag, string(body))
+ }
+ reading = messages
+
+ case body = <-pending:
+ routingKey := "ignored for fanout exchanges, application dependent for other exchanges"
+ err := pub.Publish(exchange, routingKey, false, false, amqp.Publishing{
+ Body: body,
+ })
+ // Retry failed delivery on the next session
+ if err != nil {
+ pending <- body
+ pub.Close()
+ break Publish
+ }
+
+ case body, running = <-reading:
+ // all messages consumed
+ if !running {
+ return
+ }
+ // work on pending delivery until ack'd
+ pending <- body
+ reading = nil
+ }
+ }
+ }
+}
+
+// identity returns the same host/process unique string for the lifetime of
+// this process so that subscriber reconnections reuse the same queue name.
+func identity() string {
+ hostname, err := os.Hostname()
+ h := sha1.New()
+ fmt.Fprint(h, hostname)
+ fmt.Fprint(h, err)
+ fmt.Fprint(h, os.Getpid())
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
+
+// subscribe consumes deliveries from an exclusive queue from a fanout exchange and sends to the application specific messages chan.
+func subscribe(sessions chan chan session, messages chan<- message) {
+ queue := identity()
+
+ for session := range sessions {
+ sub := <-session
+
+ if _, err := sub.QueueDeclare(queue, false, true, true, false, nil); err != nil {
+ log.Printf("cannot consume from exclusive queue: %q, %v", queue, err)
+ return
+ }
+
+ routingKey := "application specific routing key for fancy toplogies"
+ if err := sub.QueueBind(queue, routingKey, exchange, false, nil); err != nil {
+ log.Printf("cannot consume without a binding to exchange: %q, %v", exchange, err)
+ return
+ }
+
+ deliveries, err := sub.Consume(queue, "", false, true, false, false, nil)
+ if err != nil {
+ log.Printf("cannot consume from: %q, %v", queue, err)
+ return
+ }
+
+ log.Printf("subscribed...")
+
+ for msg := range deliveries {
+ messages <- message(msg.Body)
+ sub.Ack(msg.DeliveryTag, false)
+ }
+ }
+}
+
+// read is this application's translation to the message format, scanning from
+// stdin.
+func read(r io.Reader) <-chan message {
+ lines := make(chan message)
+ go func() {
+ defer close(lines)
+ scan := bufio.NewScanner(r)
+ for scan.Scan() {
+ lines <- message(scan.Bytes())
+ }
+ }()
+ return lines
+}
+
+// write is this application's subscriber of application messages, printing to
+// stdout.
+func write(w io.Writer) chan<- message {
+ lines := make(chan message)
+ go func() {
+ for line := range lines {
+ fmt.Fprintln(w, string(line))
+ }
+ }()
+ return lines
+}
+
+func main() {
+ flag.Parse()
+
+ ctx, done := context.WithCancel(context.Background())
+
+ go func() {
+ publish(redial(ctx, *url), read(os.Stdin))
+ done()
+ }()
+
+ go func() {
+ subscribe(redial(ctx, *url), write(os.Stdout))
+ done()
+ }()
+
+ <-ctx.Done()
+}
diff --git a/vendor/github.com/streadway/amqp/_examples/simple-consumer/consumer.go b/vendor/github.com/streadway/amqp/_examples/simple-consumer/consumer.go
new file mode 100644
index 00000000..03d30deb
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/_examples/simple-consumer/consumer.go
@@ -0,0 +1,169 @@
+// This example declares a durable Exchange, an ephemeral (auto-delete) Queue,
+// binds the Queue to the Exchange with a binding key, and consumes every
+// message published to that Exchange with that routing key.
+//
+package main
+
+import (
+ "flag"
+ "fmt"
+ "github.com/streadway/amqp"
+ "log"
+ "time"
+)
+
+var (
+ uri = flag.String("uri", "amqp://guest:guest@localhost:5672/", "AMQP URI")
+ exchange = flag.String("exchange", "test-exchange", "Durable, non-auto-deleted AMQP exchange name")
+ exchangeType = flag.String("exchange-type", "direct", "Exchange type - direct|fanout|topic|x-custom")
+ queue = flag.String("queue", "test-queue", "Ephemeral AMQP queue name")
+ bindingKey = flag.String("key", "test-key", "AMQP binding key")
+ consumerTag = flag.String("consumer-tag", "simple-consumer", "AMQP consumer tag (should not be blank)")
+ lifetime = flag.Duration("lifetime", 5*time.Second, "lifetime of process before shutdown (0s=infinite)")
+)
+
+func init() {
+ flag.Parse()
+}
+
+func main() {
+ c, err := NewConsumer(*uri, *exchange, *exchangeType, *queue, *bindingKey, *consumerTag)
+ if err != nil {
+ log.Fatalf("%s", err)
+ }
+
+ if *lifetime > 0 {
+ log.Printf("running for %s", *lifetime)
+ time.Sleep(*lifetime)
+ } else {
+ log.Printf("running forever")
+ select {}
+ }
+
+ log.Printf("shutting down")
+
+ if err := c.Shutdown(); err != nil {
+ log.Fatalf("error during shutdown: %s", err)
+ }
+}
+
+type Consumer struct {
+ conn *amqp.Connection
+ channel *amqp.Channel
+ tag string
+ done chan error
+}
+
+func NewConsumer(amqpURI, exchange, exchangeType, queueName, key, ctag string) (*Consumer, error) {
+ c := &Consumer{
+ conn: nil,
+ channel: nil,
+ tag: ctag,
+ done: make(chan error),
+ }
+
+ var err error
+
+ log.Printf("dialing %q", amqpURI)
+ c.conn, err = amqp.Dial(amqpURI)
+ if err != nil {
+ return nil, fmt.Errorf("Dial: %s", err)
+ }
+
+ go func() {
+ fmt.Printf("closing: %s", <-c.conn.NotifyClose(make(chan *amqp.Error)))
+ }()
+
+ log.Printf("got Connection, getting Channel")
+ c.channel, err = c.conn.Channel()
+ if err != nil {
+ return nil, fmt.Errorf("Channel: %s", err)
+ }
+
+ log.Printf("got Channel, declaring Exchange (%q)", exchange)
+ if err = c.channel.ExchangeDeclare(
+ exchange, // name of the exchange
+ exchangeType, // type
+ true, // durable
+ false, // delete when complete
+ false, // internal
+ false, // noWait
+ nil, // arguments
+ ); err != nil {
+ return nil, fmt.Errorf("Exchange Declare: %s", err)
+ }
+
+ log.Printf("declared Exchange, declaring Queue %q", queueName)
+ queue, err := c.channel.QueueDeclare(
+ queueName, // name of the queue
+ true, // durable
+ false, // delete when unused
+ false, // exclusive
+ false, // noWait
+ nil, // arguments
+ )
+ if err != nil {
+ return nil, fmt.Errorf("Queue Declare: %s", err)
+ }
+
+ log.Printf("declared Queue (%q %d messages, %d consumers), binding to Exchange (key %q)",
+ queue.Name, queue.Messages, queue.Consumers, key)
+
+ if err = c.channel.QueueBind(
+ queue.Name, // name of the queue
+ key, // bindingKey
+ exchange, // sourceExchange
+ false, // noWait
+ nil, // arguments
+ ); err != nil {
+ return nil, fmt.Errorf("Queue Bind: %s", err)
+ }
+
+ log.Printf("Queue bound to Exchange, starting Consume (consumer tag %q)", c.tag)
+ deliveries, err := c.channel.Consume(
+ queue.Name, // name
+ c.tag, // consumerTag,
+ false, // noAck
+ false, // exclusive
+ false, // noLocal
+ false, // noWait
+ nil, // arguments
+ )
+ if err != nil {
+ return nil, fmt.Errorf("Queue Consume: %s", err)
+ }
+
+ go handle(deliveries, c.done)
+
+ return c, nil
+}
+
+func (c *Consumer) Shutdown() error {
+ // will close() the deliveries channel
+ if err := c.channel.Cancel(c.tag, true); err != nil {
+ return fmt.Errorf("Consumer cancel failed: %s", err)
+ }
+
+ if err := c.conn.Close(); err != nil {
+ return fmt.Errorf("AMQP connection close error: %s", err)
+ }
+
+ defer log.Printf("AMQP shutdown OK")
+
+ // wait for handle() to exit
+ return <-c.done
+}
+
+func handle(deliveries <-chan amqp.Delivery, done chan error) {
+ for d := range deliveries {
+ log.Printf(
+ "got %dB delivery: [%v] %q",
+ len(d.Body),
+ d.DeliveryTag,
+ d.Body,
+ )
+ d.Ack(false)
+ }
+ log.Printf("handle: deliveries channel closed")
+ done <- nil
+}
diff --git a/vendor/github.com/streadway/amqp/_examples/simple-producer/producer.go b/vendor/github.com/streadway/amqp/_examples/simple-producer/producer.go
new file mode 100644
index 00000000..19986839
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/_examples/simple-producer/producer.go
@@ -0,0 +1,112 @@
+// This example declares a durable Exchange, and publishes a single message to
+// that Exchange with a given routing key.
+//
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+
+ "github.com/streadway/amqp"
+)
+
+var (
+ uri = flag.String("uri", "amqp://guest:guest@localhost:5672/", "AMQP URI")
+ exchangeName = flag.String("exchange", "test-exchange", "Durable AMQP exchange name")
+ exchangeType = flag.String("exchange-type", "direct", "Exchange type - direct|fanout|topic|x-custom")
+ routingKey = flag.String("key", "test-key", "AMQP routing key")
+ body = flag.String("body", "foobar", "Body of message")
+ reliable = flag.Bool("reliable", true, "Wait for the publisher confirmation before exiting")
+)
+
+func init() {
+ flag.Parse()
+}
+
+func main() {
+ if err := publish(*uri, *exchangeName, *exchangeType, *routingKey, *body, *reliable); err != nil {
+ log.Fatalf("%s", err)
+ }
+ log.Printf("published %dB OK", len(*body))
+}
+
+func publish(amqpURI, exchange, exchangeType, routingKey, body string, reliable bool) error {
+
+ // This function dials, connects, declares, publishes, and tears down,
+ // all in one go. In a real service, you probably want to maintain a
+ // long-lived connection as state, and publish against that.
+
+ log.Printf("dialing %q", amqpURI)
+ connection, err := amqp.Dial(amqpURI)
+ if err != nil {
+ return fmt.Errorf("Dial: %s", err)
+ }
+ defer connection.Close()
+
+ log.Printf("got Connection, getting Channel")
+ channel, err := connection.Channel()
+ if err != nil {
+ return fmt.Errorf("Channel: %s", err)
+ }
+
+ log.Printf("got Channel, declaring %q Exchange (%q)", exchangeType, exchange)
+ if err := channel.ExchangeDeclare(
+ exchange, // name
+ exchangeType, // type
+ true, // durable
+ false, // auto-deleted
+ false, // internal
+ false, // noWait
+ nil, // arguments
+ ); err != nil {
+ return fmt.Errorf("Exchange Declare: %s", err)
+ }
+
+ // Reliable publisher confirms require confirm.select support from the
+ // connection.
+ if reliable {
+ log.Printf("enabling publishing confirms.")
+ if err := channel.Confirm(false); err != nil {
+ return fmt.Errorf("Channel could not be put into confirm mode: %s", err)
+ }
+
+ confirms := channel.NotifyPublish(make(chan amqp.Confirmation, 1))
+
+ defer confirmOne(confirms)
+ }
+
+ log.Printf("declared Exchange, publishing %dB body (%q)", len(body), body)
+ if err = channel.Publish(
+ exchange, // publish to an exchange
+ routingKey, // routing to 0 or more queues
+ false, // mandatory
+ false, // immediate
+ amqp.Publishing{
+ Headers: amqp.Table{},
+ ContentType: "text/plain",
+ ContentEncoding: "",
+ Body: []byte(body),
+ DeliveryMode: amqp.Transient, // 1=non-persistent, 2=persistent
+ Priority: 0, // 0-9
+ // a bunch of application/implementation-specific fields
+ },
+ ); err != nil {
+ return fmt.Errorf("Exchange Publish: %s", err)
+ }
+
+ return nil
+}
+
+// One would typically keep a channel of publishings, a sequence number, and a
+// set of unacknowledged sequence numbers and loop until the publishing channel
+// is closed.
+func confirmOne(confirms <-chan amqp.Confirmation) {
+ log.Printf("waiting for confirmation of one publishing")
+
+ if confirmed := <-confirms; confirmed.Ack {
+ log.Printf("confirmed delivery with delivery tag: %d", confirmed.DeliveryTag)
+ } else {
+ log.Printf("failed delivery of delivery tag: %d", confirmed.DeliveryTag)
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/allocator.go b/vendor/github.com/streadway/amqp/allocator.go
new file mode 100644
index 00000000..53620e7d
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/allocator.go
@@ -0,0 +1,106 @@
+package amqp
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+)
+
+const (
+ free = 0
+ allocated = 1
+)
+
+// allocator maintains a bitset of allocated numbers.
+type allocator struct {
+ pool *big.Int
+ last int
+ low int
+ high int
+}
+
+// NewAllocator reserves and frees integers out of a range between low and
+// high.
+//
+// O(N) worst case space used, where N is maximum allocated, divided by
+// sizeof(big.Word)
+func newAllocator(low, high int) *allocator {
+ return &allocator{
+ pool: big.NewInt(0),
+ last: low,
+ low: low,
+ high: high,
+ }
+}
+
+// String returns a string describing the contents of the allocator like
+// "allocator[low..high] reserved..until"
+//
+// O(N) where N is high-low
+func (a allocator) String() string {
+ b := &bytes.Buffer{}
+ fmt.Fprintf(b, "allocator[%d..%d]", a.low, a.high)
+
+ for low := a.low; low <= a.high; low++ {
+ high := low
+ for a.reserved(high) && high <= a.high {
+ high++
+ }
+
+ if high > low+1 {
+ fmt.Fprintf(b, " %d..%d", low, high-1)
+ } else if high > low {
+ fmt.Fprintf(b, " %d", high-1)
+ }
+
+ low = high
+ }
+ return b.String()
+}
+
+// Next reserves and returns the next available number out of the range between
+// low and high. If no number is available, false is returned.
+//
+// O(N) worst case runtime where N is allocated, but usually O(1) due to a
+// rolling index into the oldest allocation.
+func (a *allocator) next() (int, bool) {
+ wrapped := a.last
+
+ // Find trailing bit
+ for ; a.last <= a.high; a.last++ {
+ if a.reserve(a.last) {
+ return a.last, true
+ }
+ }
+
+ // Find preceding free'd pool
+ a.last = a.low
+
+ for ; a.last < wrapped; a.last++ {
+ if a.reserve(a.last) {
+ return a.last, true
+ }
+ }
+
+ return 0, false
+}
+
+// reserve claims the bit if it is not already claimed, returning true if
+// successfully claimed.
+func (a *allocator) reserve(n int) bool {
+ if a.reserved(n) {
+ return false
+ }
+ a.pool.SetBit(a.pool, n-a.low, allocated)
+ return true
+}
+
+// reserved returns true if the integer has been allocated
+func (a *allocator) reserved(n int) bool {
+ return a.pool.Bit(n-a.low) == allocated
+}
+
+// release frees the use of the number for another allocation
+func (a *allocator) release(n int) {
+ a.pool.SetBit(a.pool, n-a.low, free)
+}
diff --git a/vendor/github.com/streadway/amqp/allocator_test.go b/vendor/github.com/streadway/amqp/allocator_test.go
new file mode 100644
index 00000000..1a4e7de7
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/allocator_test.go
@@ -0,0 +1,94 @@
+package amqp
+
+import (
+ "math/rand"
+ "testing"
+)
+
+func TestAllocatorFirstShouldBeTheLow(t *testing.T) {
+ n, ok := newAllocator(1, 2).next()
+ if !ok {
+ t.Fatalf("expected to allocate between 1 and 2")
+ }
+
+ if want, got := 1, n; want != got {
+ t.Fatalf("expected to first allocation to be 1")
+ }
+}
+
+func TestAllocatorShouldBeBoundByHigh(t *testing.T) {
+ a := newAllocator(1, 2)
+
+ if n, ok := a.next(); n != 1 || !ok {
+ t.Fatalf("expected to allocate between 1 and 2, got %d, %v", n, ok)
+ }
+ if n, ok := a.next(); n != 2 || !ok {
+ t.Fatalf("expected to allocate between 1 and 2, got %d, %v", n, ok)
+ }
+ if _, ok := a.next(); ok {
+ t.Fatalf("expected not to allocate outside of 1 and 2")
+ }
+}
+
+func TestAllocatorStringShouldIncludeAllocatedRanges(t *testing.T) {
+ a := newAllocator(1, 10)
+ a.reserve(1)
+ a.reserve(2)
+ a.reserve(3)
+ a.reserve(5)
+ a.reserve(6)
+ a.reserve(8)
+ a.reserve(10)
+
+ if want, got := "allocator[1..10] 1..3 5..6 8 10", a.String(); want != got {
+ t.Fatalf("expected String of %q, got %q", want, got)
+ }
+}
+
+func TestAllocatorShouldReuseReleased(t *testing.T) {
+ a := newAllocator(1, 2)
+
+ first, _ := a.next()
+ if want, got := 1, first; want != got {
+ t.Fatalf("expected allocation to be %d, got: %d", want, got)
+ }
+
+ second, _ := a.next()
+ if want, got := 2, second; want != got {
+ t.Fatalf("expected allocation to be %d, got: %d", want, got)
+ }
+
+ a.release(first)
+
+ third, _ := a.next()
+ if want, got := first, third; want != got {
+ t.Fatalf("expected third allocation to be %d, got: %d", want, got)
+ }
+
+ _, ok := a.next()
+ if want, got := false, ok; want != got {
+ t.Fatalf("expected fourth allocation to saturate the pool")
+ }
+}
+
+func TestAllocatorReleasesKeepUpWithAllocationsForAllSizes(t *testing.T) {
+ if testing.Short() {
+ t.Skip()
+ }
+
+ const runs = 5
+ const max = 13
+
+ for lim := 1; lim < 2<= lim { // fills the allocator
+ a.release(int(rand.Int63n(int64(lim))))
+ }
+ if _, ok := a.next(); !ok {
+ t.Fatalf("expected %d runs of random release of size %d not to fail on allocation %d", runs, lim, i)
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/auth.go b/vendor/github.com/streadway/amqp/auth.go
new file mode 100644
index 00000000..ebc765b6
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/auth.go
@@ -0,0 +1,46 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "fmt"
+)
+
+// Authentication interface provides a means for different SASL authentication
+// mechanisms to be used during connection tuning.
+type Authentication interface {
+ Mechanism() string
+ Response() string
+}
+
+// PlainAuth is a similar to Basic Auth in HTTP.
+type PlainAuth struct {
+ Username string
+ Password string
+}
+
+// Mechanism returns "PLAIN"
+func (auth *PlainAuth) Mechanism() string {
+ return "PLAIN"
+}
+
+// Response returns the null character delimited encoding for the SASL PLAIN Mechanism.
+func (auth *PlainAuth) Response() string {
+ return fmt.Sprintf("\000%s\000%s", auth.Username, auth.Password)
+}
+
+// Finds the first mechanism preferred by the client that the server supports.
+func pickSASLMechanism(client []Authentication, serverMechanisms []string) (auth Authentication, ok bool) {
+ for _, auth = range client {
+ for _, mech := range serverMechanisms {
+ if auth.Mechanism() == mech {
+ return auth, true
+ }
+ }
+ }
+
+ return
+}
diff --git a/vendor/github.com/streadway/amqp/certs.sh b/vendor/github.com/streadway/amqp/certs.sh
new file mode 100755
index 00000000..834f4224
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/certs.sh
@@ -0,0 +1,159 @@
+#!/bin/sh
+#
+# Creates the CA, server and client certs to be used by tls_test.go
+# http://www.rabbitmq.com/ssl.html
+#
+# Copy stdout into the const section of tls_test.go or use for RabbitMQ
+#
+root=$PWD/certs
+
+if [ -f $root/ca/serial ]; then
+ echo >&2 "Previous installation found"
+ echo >&2 "Remove $root/ca and rerun to overwrite"
+ exit 1
+fi
+
+mkdir -p $root/ca/private
+mkdir -p $root/ca/certs
+mkdir -p $root/server
+mkdir -p $root/client
+
+cd $root/ca
+
+chmod 700 private
+touch index.txt
+echo 'unique_subject = no' > index.txt.attr
+echo '01' > serial
+echo >openssl.cnf '
+[ ca ]
+default_ca = testca
+
+[ testca ]
+dir = .
+certificate = $dir/cacert.pem
+database = $dir/index.txt
+new_certs_dir = $dir/certs
+private_key = $dir/private/cakey.pem
+serial = $dir/serial
+
+default_crl_days = 7
+default_days = 3650
+default_md = sha1
+
+policy = testca_policy
+x509_extensions = certificate_extensions
+
+[ testca_policy ]
+commonName = supplied
+stateOrProvinceName = optional
+countryName = optional
+emailAddress = optional
+organizationName = optional
+organizationalUnitName = optional
+
+[ certificate_extensions ]
+basicConstraints = CA:false
+
+[ req ]
+default_bits = 2048
+default_keyfile = ./private/cakey.pem
+default_md = sha1
+prompt = yes
+distinguished_name = root_ca_distinguished_name
+x509_extensions = root_ca_extensions
+
+[ root_ca_distinguished_name ]
+commonName = hostname
+
+[ root_ca_extensions ]
+basicConstraints = CA:true
+keyUsage = keyCertSign, cRLSign
+
+[ client_ca_extensions ]
+basicConstraints = CA:false
+keyUsage = digitalSignature
+extendedKeyUsage = 1.3.6.1.5.5.7.3.2
+
+[ server_ca_extensions ]
+basicConstraints = CA:false
+keyUsage = keyEncipherment
+extendedKeyUsage = 1.3.6.1.5.5.7.3.1
+subjectAltName = @alt_names
+
+[ alt_names ]
+IP.1 = 127.0.0.1
+'
+
+openssl req \
+ -x509 \
+ -nodes \
+ -config openssl.cnf \
+ -newkey rsa:2048 \
+ -days 3650 \
+ -subj "/CN=MyTestCA/" \
+ -out cacert.pem \
+ -outform PEM
+
+openssl x509 \
+ -in cacert.pem \
+ -out cacert.cer \
+ -outform DER
+
+openssl genrsa -out $root/server/key.pem 2048
+openssl genrsa -out $root/client/key.pem 2048
+
+openssl req \
+ -new \
+ -nodes \
+ -config openssl.cnf \
+ -subj "/CN=127.0.0.1/O=server/" \
+ -key $root/server/key.pem \
+ -out $root/server/req.pem \
+ -outform PEM
+
+openssl req \
+ -new \
+ -nodes \
+ -config openssl.cnf \
+ -subj "/CN=127.0.0.1/O=client/" \
+ -key $root/client/key.pem \
+ -out $root/client/req.pem \
+ -outform PEM
+
+openssl ca \
+ -config openssl.cnf \
+ -in $root/server/req.pem \
+ -out $root/server/cert.pem \
+ -notext \
+ -batch \
+ -extensions server_ca_extensions
+
+openssl ca \
+ -config openssl.cnf \
+ -in $root/client/req.pem \
+ -out $root/client/cert.pem \
+ -notext \
+ -batch \
+ -extensions client_ca_extensions
+
+cat <<-END
+const caCert = \`
+`cat $root/ca/cacert.pem`
+\`
+
+const serverCert = \`
+`cat $root/server/cert.pem`
+\`
+
+const serverKey = \`
+`cat $root/server/key.pem`
+\`
+
+const clientCert = \`
+`cat $root/client/cert.pem`
+\`
+
+const clientKey = \`
+`cat $root/client/key.pem`
+\`
+END
diff --git a/vendor/github.com/streadway/amqp/channel.go b/vendor/github.com/streadway/amqp/channel.go
new file mode 100644
index 00000000..2bf43516
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/channel.go
@@ -0,0 +1,1538 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "reflect"
+ "sync"
+)
+
+// 0 1 3 7 size+7 size+8
+// +------+---------+-------------+ +------------+ +-----------+
+// | type | channel | size | | payload | | frame-end |
+// +------+---------+-------------+ +------------+ +-----------+
+// octet short long size octets octet
+const frameHeaderSize = 1 + 2 + 4 + 1
+
+/*
+Channel represents an AMQP channel. Used as a context for valid message
+exchange. Errors on methods with this Channel as a receiver means this channel
+should be discarded and a new channel established.
+
+*/
+type Channel struct {
+ destructor sync.Once
+ sendM sync.Mutex // sequence channel frames
+ m sync.Mutex // struct field mutex
+ confirmM sync.Mutex // publisher confirms state mutex
+
+ connection *Connection
+
+ rpc chan message
+ consumers *consumers
+
+ id uint16
+
+ // true when we will never notify again
+ noNotify bool
+
+ // Channel and Connection exceptions will be broadcast on these listeners.
+ closes []chan *Error
+
+ // Listeners for active=true flow control. When true is sent to a listener,
+ // publishing should pause until false is sent to listeners.
+ flows []chan bool
+
+ // Listeners for returned publishings for unroutable messages on mandatory
+ // publishings or undeliverable messages on immediate publishings.
+ returns []chan Return
+
+ // Listeners for when the server notifies the client that
+ // a consumer has been cancelled.
+ cancels []chan string
+
+ // Allocated when in confirm mode in order to track publish counter and order confirms
+ confirms *confirms
+ confirming bool
+
+ // Selects on any errors from shutdown during RPC
+ errors chan *Error
+
+ // State machine that manages frame order, must only be mutated by the connection
+ recv func(*Channel, frame) error
+
+ // State that manages the send behavior after before and after shutdown, must
+ // only be mutated in shutdown()
+ send func(*Channel, message) error
+
+ // Current state for frame re-assembly, only mutated from recv
+ message messageWithContent
+ header *headerFrame
+ body []byte
+}
+
+// Constructs a new channel with the given framing rules
+func newChannel(c *Connection, id uint16) *Channel {
+ return &Channel{
+ connection: c,
+ id: id,
+ rpc: make(chan message),
+ consumers: makeConsumers(),
+ confirms: newConfirms(),
+ recv: (*Channel).recvMethod,
+ send: (*Channel).sendOpen,
+ errors: make(chan *Error, 1),
+ }
+}
+
+// shutdown is called by Connection after the channel has been removed from the
+// connection registry.
+func (ch *Channel) shutdown(e *Error) {
+ ch.destructor.Do(func() {
+ ch.m.Lock()
+ defer ch.m.Unlock()
+
+ // Broadcast abnormal shutdown
+ if e != nil {
+ for _, c := range ch.closes {
+ c <- e
+ }
+ }
+
+ ch.send = (*Channel).sendClosed
+
+ // Notify RPC if we're selecting
+ if e != nil {
+ ch.errors <- e
+ }
+
+ ch.consumers.closeAll()
+
+ for _, c := range ch.closes {
+ close(c)
+ }
+
+ for _, c := range ch.flows {
+ close(c)
+ }
+
+ for _, c := range ch.returns {
+ close(c)
+ }
+
+ for _, c := range ch.cancels {
+ close(c)
+ }
+
+ if ch.confirms != nil {
+ ch.confirms.Close()
+ }
+
+ ch.noNotify = true
+ })
+}
+
+func (ch *Channel) open() error {
+ return ch.call(&channelOpen{}, &channelOpenOk{})
+}
+
+// Performs a request/response call for when the message is not NoWait and is
+// specified as Synchronous.
+func (ch *Channel) call(req message, res ...message) error {
+ if err := ch.send(ch, req); err != nil {
+ return err
+ }
+
+ if req.wait() {
+ select {
+ case e := <-ch.errors:
+ return e
+
+ case msg := <-ch.rpc:
+ if msg != nil {
+ for _, try := range res {
+ if reflect.TypeOf(msg) == reflect.TypeOf(try) {
+ // *res = *msg
+ vres := reflect.ValueOf(try).Elem()
+ vmsg := reflect.ValueOf(msg).Elem()
+ vres.Set(vmsg)
+ return nil
+ }
+ }
+ return ErrCommandInvalid
+ }
+ // RPC channel has been closed without an error, likely due to a hard
+ // error on the Connection. This indicates we have already been
+ // shutdown and if were waiting, will have returned from the errors chan.
+ return ErrClosed
+ }
+ }
+
+ return nil
+}
+
+func (ch *Channel) sendClosed(msg message) (err error) {
+ ch.sendM.Lock()
+ defer ch.sendM.Unlock()
+
+ // After a 'channel.close' is sent or received the only valid response is
+ // channel.close-ok
+ if _, ok := msg.(*channelCloseOk); ok {
+ return ch.connection.send(&methodFrame{
+ ChannelId: ch.id,
+ Method: msg,
+ })
+ }
+
+ return ErrClosed
+}
+
+func (ch *Channel) sendOpen(msg message) (err error) {
+ ch.sendM.Lock()
+ defer ch.sendM.Unlock()
+
+ if content, ok := msg.(messageWithContent); ok {
+ props, body := content.getContent()
+ class, _ := content.id()
+
+ // catch client max frame size==0 and server max frame size==0
+ // set size to length of what we're trying to publish
+ var size int
+ if ch.connection.Config.FrameSize > 0 {
+ size = ch.connection.Config.FrameSize - frameHeaderSize
+ } else {
+ size = len(body)
+ }
+
+ if err = ch.connection.send(&methodFrame{
+ ChannelId: ch.id,
+ Method: content,
+ }); err != nil {
+ return
+ }
+
+ if err = ch.connection.send(&headerFrame{
+ ChannelId: ch.id,
+ ClassId: class,
+ Size: uint64(len(body)),
+ Properties: props,
+ }); err != nil {
+ return
+ }
+
+ // chunk body into size (max frame size - frame header size)
+ for i, j := 0, size; i < len(body); i, j = j, j+size {
+ if j > len(body) {
+ j = len(body)
+ }
+
+ if err = ch.connection.send(&bodyFrame{
+ ChannelId: ch.id,
+ Body: body[i:j],
+ }); err != nil {
+ return
+ }
+ }
+ } else {
+ err = ch.connection.send(&methodFrame{
+ ChannelId: ch.id,
+ Method: msg,
+ })
+ }
+
+ return
+}
+
+// Eventually called via the state machine from the connection's reader
+// goroutine, so assumes serialized access.
+func (ch *Channel) dispatch(msg message) {
+ switch m := msg.(type) {
+ case *channelClose:
+ ch.connection.closeChannel(ch, newError(m.ReplyCode, m.ReplyText))
+ ch.send(ch, &channelCloseOk{})
+
+ case *channelFlow:
+ for _, c := range ch.flows {
+ c <- m.Active
+ }
+ ch.send(ch, &channelFlowOk{Active: m.Active})
+
+ case *basicCancel:
+ for _, c := range ch.cancels {
+ c <- m.ConsumerTag
+ }
+ ch.consumers.close(m.ConsumerTag)
+
+ case *basicReturn:
+ ret := newReturn(*m)
+ for _, c := range ch.returns {
+ c <- *ret
+ }
+
+ case *basicAck:
+ if ch.confirming {
+ if m.Multiple {
+ ch.confirms.Multiple(Confirmation{m.DeliveryTag, true})
+ } else {
+ ch.confirms.One(Confirmation{m.DeliveryTag, true})
+ }
+ }
+
+ case *basicNack:
+ if ch.confirming {
+ if m.Multiple {
+ ch.confirms.Multiple(Confirmation{m.DeliveryTag, false})
+ } else {
+ ch.confirms.One(Confirmation{m.DeliveryTag, false})
+ }
+ }
+
+ case *basicDeliver:
+ ch.consumers.send(m.ConsumerTag, newDelivery(ch, m))
+ // TODO log failed consumer and close channel, this can happen when
+ // deliveries are in flight and a no-wait cancel has happened
+
+ default:
+ ch.rpc <- msg
+ }
+}
+
+func (ch *Channel) transition(f func(*Channel, frame) error) error {
+ ch.recv = f
+ return nil
+}
+
+func (ch *Channel) recvMethod(f frame) error {
+ switch frame := f.(type) {
+ case *methodFrame:
+ if msg, ok := frame.Method.(messageWithContent); ok {
+ ch.body = make([]byte, 0)
+ ch.message = msg
+ return ch.transition((*Channel).recvHeader)
+ }
+
+ ch.dispatch(frame.Method) // termination state
+ return ch.transition((*Channel).recvMethod)
+
+ case *headerFrame:
+ // drop
+ return ch.transition((*Channel).recvMethod)
+
+ case *bodyFrame:
+ // drop
+ return ch.transition((*Channel).recvMethod)
+ }
+
+ panic("unexpected frame type")
+}
+
+func (ch *Channel) recvHeader(f frame) error {
+ switch frame := f.(type) {
+ case *methodFrame:
+ // interrupt content and handle method
+ return ch.recvMethod(f)
+
+ case *headerFrame:
+ // start collecting if we expect body frames
+ ch.header = frame
+
+ if frame.Size == 0 {
+ ch.message.setContent(ch.header.Properties, ch.body)
+ ch.dispatch(ch.message) // termination state
+ return ch.transition((*Channel).recvMethod)
+ }
+ return ch.transition((*Channel).recvContent)
+
+ case *bodyFrame:
+ // drop and reset
+ return ch.transition((*Channel).recvMethod)
+ }
+
+ panic("unexpected frame type")
+}
+
+// state after method + header and before the length
+// defined by the header has been reached
+func (ch *Channel) recvContent(f frame) error {
+ switch frame := f.(type) {
+ case *methodFrame:
+ // interrupt content and handle method
+ return ch.recvMethod(f)
+
+ case *headerFrame:
+ // drop and reset
+ return ch.transition((*Channel).recvMethod)
+
+ case *bodyFrame:
+ ch.body = append(ch.body, frame.Body...)
+
+ if uint64(len(ch.body)) >= ch.header.Size {
+ ch.message.setContent(ch.header.Properties, ch.body)
+ ch.dispatch(ch.message) // termination state
+ return ch.transition((*Channel).recvMethod)
+ }
+
+ return ch.transition((*Channel).recvContent)
+ }
+
+ panic("unexpected frame type")
+}
+
+/*
+Close initiate a clean channel closure by sending a close message with the error
+code set to '200'.
+
+It is safe to call this method multiple times.
+
+*/
+func (ch *Channel) Close() error {
+ defer ch.connection.closeChannel(ch, nil)
+ return ch.call(
+ &channelClose{ReplyCode: replySuccess},
+ &channelCloseOk{},
+ )
+}
+
+/*
+NotifyClose registers a listener for when the server sends a channel or
+connection exception in the form of a Connection.Close or Channel.Close method.
+Connection exceptions will be broadcast to all open channels and all channels
+will be closed, where channel exceptions will only be broadcast to listeners to
+this channel.
+
+The chan provided will be closed when the Channel is closed and on a
+graceful close, no error will be sent.
+
+*/
+func (ch *Channel) NotifyClose(c chan *Error) chan *Error {
+ ch.m.Lock()
+ defer ch.m.Unlock()
+
+ if ch.noNotify {
+ close(c)
+ } else {
+ ch.closes = append(ch.closes, c)
+ }
+
+ return c
+}
+
+/*
+NotifyFlow registers a listener for basic.flow methods sent by the server.
+When `true` is sent on one of the listener channels, all publishers should
+pause until a `false` is sent.
+
+The server may ask the producer to pause or restart the flow of Publishings
+sent by on a channel. This is a simple flow-control mechanism that a server can
+use to avoid overflowing its queues or otherwise finding itself receiving more
+messages than it can process. Note that this method is not intended for window
+control. It does not affect contents returned by basic.get-ok methods.
+
+When a new channel is opened, it is active (flow is active). Some
+applications assume that channels are inactive until started. To emulate
+this behavior a client MAY open the channel, then pause it.
+
+Publishers should respond to a flow messages as rapidly as possible and the
+server may disconnect over producing channels that do not respect these
+messages.
+
+basic.flow-ok methods will always be returned to the server regardless of
+the number of listeners there are.
+
+To control the flow of deliveries from the server, use the Channel.Flow()
+method instead.
+
+Note: RabbitMQ will rather use TCP pushback on the network connection instead
+of sending basic.flow. This means that if a single channel is producing too
+much on the same connection, all channels using that connection will suffer,
+including acknowledgments from deliveries. Use different Connections if you
+desire to interleave consumers and producers in the same process to avoid your
+basic.ack messages from getting rate limited with your basic.publish messages.
+
+*/
+func (ch *Channel) NotifyFlow(c chan bool) chan bool {
+ ch.m.Lock()
+ defer ch.m.Unlock()
+
+ if ch.noNotify {
+ close(c)
+ } else {
+ ch.flows = append(ch.flows, c)
+ }
+
+ return c
+}
+
+/*
+NotifyReturn registers a listener for basic.return methods. These can be sent
+from the server when a publish is undeliverable either from the mandatory or
+immediate flags.
+
+A return struct has a copy of the Publishing along with some error
+information about why the publishing failed.
+
+*/
+func (ch *Channel) NotifyReturn(c chan Return) chan Return {
+ ch.m.Lock()
+ defer ch.m.Unlock()
+
+ if ch.noNotify {
+ close(c)
+ } else {
+ ch.returns = append(ch.returns, c)
+ }
+
+ return c
+}
+
+/*
+NotifyCancel registers a listener for basic.cancel methods. These can be sent
+from the server when a queue is deleted or when consuming from a mirrored queue
+where the master has just failed (and was moved to another node).
+
+The subscription tag is returned to the listener.
+
+*/
+func (ch *Channel) NotifyCancel(c chan string) chan string {
+ ch.m.Lock()
+ defer ch.m.Unlock()
+
+ if ch.noNotify {
+ close(c)
+ } else {
+ ch.cancels = append(ch.cancels, c)
+ }
+
+ return c
+}
+
+/*
+NotifyConfirm calls NotifyPublish and starts a goroutine sending
+ordered Ack and Nack DeliveryTag to the respective channels.
+
+For strict ordering, use NotifyPublish instead.
+*/
+func (ch *Channel) NotifyConfirm(ack, nack chan uint64) (chan uint64, chan uint64) {
+ confirms := ch.NotifyPublish(make(chan Confirmation, len(ack)+len(nack)))
+
+ go func() {
+ for c := range confirms {
+ if c.Ack {
+ ack <- c.DeliveryTag
+ } else {
+ nack <- c.DeliveryTag
+ }
+ }
+ close(ack)
+ if nack != ack {
+ close(nack)
+ }
+ }()
+
+ return ack, nack
+}
+
+/*
+NotifyPublish registers a listener for reliable publishing. Receives from this
+chan for every publish after Channel.Confirm will be in order starting with
+DeliveryTag 1.
+
+There will be one and only one Confirmation Publishing starting with the
+delivery tag of 1 and progressing sequentially until the total number of
+Publishings have been seen by the server.
+
+Acknowledgments will be received in the order of delivery from the
+NotifyPublish channels even if the server acknowledges them out of order.
+
+The listener chan will be closed when the Channel is closed.
+
+The capacity of the chan Confirmation must be at least as large as the
+number of outstanding publishings. Not having enough buffered chans will
+create a deadlock if you attempt to perform other operations on the Connection
+or Channel while confirms are in-flight.
+
+It's advisable to wait for all Confirmations to arrive before calling
+Channel.Close() or Connection.Close().
+
+*/
+func (ch *Channel) NotifyPublish(confirm chan Confirmation) chan Confirmation {
+ ch.m.Lock()
+ defer ch.m.Unlock()
+
+ if ch.noNotify {
+ close(confirm)
+ } else {
+ ch.confirms.Listen(confirm)
+ }
+
+ return confirm
+
+}
+
+/*
+Qos controls how many messages or how many bytes the server will try to keep on
+the network for consumers before receiving delivery acks. The intent of Qos is
+to make sure the network buffers stay full between the server and client.
+
+With a prefetch count greater than zero, the server will deliver that many
+messages to consumers before acknowledgments are received. The server ignores
+this option when consumers are started with noAck because no acknowledgments
+are expected or sent.
+
+With a prefetch size greater than zero, the server will try to keep at least
+that many bytes of deliveries flushed to the network before receiving
+acknowledgments from the consumers. This option is ignored when consumers are
+started with noAck.
+
+When global is true, these Qos settings apply to all existing and future
+consumers on all channels on the same connection. When false, the Channel.Qos
+settings will apply to all existing and future consumers on this channel.
+RabbitMQ does not implement the global flag.
+
+To get round-robin behavior between consumers consuming from the same queue on
+different connections, set the prefetch count to 1, and the next available
+message on the server will be delivered to the next available consumer.
+
+If your consumer work time is reasonably consistent and not much greater
+than two times your network round trip time, you will see significant
+throughput improvements starting with a prefetch count of 2 or slightly
+greater as described by benchmarks on RabbitMQ.
+
+http://www.rabbitmq.com/blog/2012/04/25/rabbitmq-performance-measurements-part-2/
+*/
+func (ch *Channel) Qos(prefetchCount, prefetchSize int, global bool) error {
+ return ch.call(
+ &basicQos{
+ PrefetchCount: uint16(prefetchCount),
+ PrefetchSize: uint32(prefetchSize),
+ Global: global,
+ },
+ &basicQosOk{},
+ )
+}
+
+/*
+Cancel stops deliveries to the consumer chan established in Channel.Consume and
+identified by consumer.
+
+Only use this method to cleanly stop receiving deliveries from the server and
+cleanly shut down the consumer chan identified by this tag. Using this method
+and waiting for remaining messages to flush from the consumer chan will ensure
+all messages received on the network will be delivered to the receiver of your
+consumer chan.
+
+Continue consuming from the chan Delivery provided by Channel.Consume until the
+chan closes.
+
+When noWait is true, do not wait for the server to acknowledge the cancel.
+Only use this when you are certain there are no deliveries in flight that
+require an acknowledgment, otherwise they will arrive and be dropped in the
+client without an ack, and will not be redelivered to other consumers.
+
+*/
+func (ch *Channel) Cancel(consumer string, noWait bool) error {
+ req := &basicCancel{
+ ConsumerTag: consumer,
+ NoWait: noWait,
+ }
+ res := &basicCancelOk{}
+
+ if err := ch.call(req, res); err != nil {
+ return err
+ }
+
+ if req.wait() {
+ ch.consumers.close(res.ConsumerTag)
+ } else {
+ // Potentially could drop deliveries in flight
+ ch.consumers.close(consumer)
+ }
+
+ return nil
+}
+
+/*
+QueueDeclare declares a queue to hold messages and deliver to consumers.
+Declaring creates a queue if it doesn't already exist, or ensures that an
+existing queue matches the same parameters.
+
+Every queue declared gets a default binding to the empty exchange "" which has
+the type "direct" with the routing key matching the queue's name. With this
+default binding, it is possible to publish messages that route directly to
+this queue by publishing to "" with the routing key of the queue name.
+
+ QueueDeclare("alerts", true, false, false, false, nil)
+ Publish("", "alerts", false, false, Publishing{Body: []byte("...")})
+
+ Delivery Exchange Key Queue
+ -----------------------------------------------
+ key: alerts -> "" -> alerts -> alerts
+
+The queue name may be empty, in which case the server will generate a unique name
+which will be returned in the Name field of Queue struct.
+
+Durable and Non-Auto-Deleted queues will survive server restarts and remain
+when there are no remaining consumers or bindings. Persistent publishings will
+be restored in this queue on server restart. These queues are only able to be
+bound to durable exchanges.
+
+Non-Durable and Auto-Deleted queues will not be redeclared on server restart
+and will be deleted by the server after a short time when the last consumer is
+canceled or the last consumer's channel is closed. Queues with this lifetime
+can also be deleted normally with QueueDelete. These durable queues can only
+be bound to non-durable exchanges.
+
+Non-Durable and Non-Auto-Deleted queues will remain declared as long as the
+server is running regardless of how many consumers. This lifetime is useful
+for temporary topologies that may have long delays between consumer activity.
+These queues can only be bound to non-durable exchanges.
+
+Durable and Auto-Deleted queues will be restored on server restart, but without
+active consumers will not survive and be removed. This Lifetime is unlikely
+to be useful.
+
+Exclusive queues are only accessible by the connection that declares them and
+will be deleted when the connection closes. Channels on other connections
+will receive an error when attempting to declare, bind, consume, purge or
+delete a queue with the same name.
+
+When noWait is true, the queue will assume to be declared on the server. A
+channel exception will arrive if the conditions are met for existing queues
+or attempting to modify an existing queue from a different connection.
+
+When the error return value is not nil, you can assume the queue could not be
+declared with these parameters, and the channel will be closed.
+
+*/
+func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
+ if err := args.Validate(); err != nil {
+ return Queue{}, err
+ }
+
+ req := &queueDeclare{
+ Queue: name,
+ Passive: false,
+ Durable: durable,
+ AutoDelete: autoDelete,
+ Exclusive: exclusive,
+ NoWait: noWait,
+ Arguments: args,
+ }
+ res := &queueDeclareOk{}
+
+ if err := ch.call(req, res); err != nil {
+ return Queue{}, err
+ }
+
+ if req.wait() {
+ return Queue{
+ Name: res.Queue,
+ Messages: int(res.MessageCount),
+ Consumers: int(res.ConsumerCount),
+ }, nil
+ }
+
+ return Queue{Name: name}, nil
+}
+
+/*
+
+QueueDeclarePassive is functionally and parametrically equivalent to
+QueueDeclare, except that it sets the "passive" attribute to true. A passive
+queue is assumed by RabbitMQ to already exist, and attempting to connect to a
+non-existent queue will cause RabbitMQ to throw an exception. This function
+can be used to test for the existence of a queue.
+
+*/
+func (ch *Channel) QueueDeclarePassive(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
+ if err := args.Validate(); err != nil {
+ return Queue{}, err
+ }
+
+ req := &queueDeclare{
+ Queue: name,
+ Passive: true,
+ Durable: durable,
+ AutoDelete: autoDelete,
+ Exclusive: exclusive,
+ NoWait: noWait,
+ Arguments: args,
+ }
+ res := &queueDeclareOk{}
+
+ if err := ch.call(req, res); err != nil {
+ return Queue{}, err
+ }
+
+ if req.wait() {
+ return Queue{
+ Name: res.Queue,
+ Messages: int(res.MessageCount),
+ Consumers: int(res.ConsumerCount),
+ }, nil
+ }
+
+ return Queue{Name: name}, nil
+}
+
+/*
+QueueInspect passively declares a queue by name to inspect the current message
+count and consumer count.
+
+Use this method to check how many unacknowledged messages reside in the queue,
+how many consumers are receiving deliveries, and whether a queue by this
+name already exists.
+
+If the queue by this name exists, use Channel.QueueDeclare check if it is
+declared with specific parameters.
+
+If a queue by this name does not exist, an error will be returned and the
+channel will be closed.
+
+*/
+func (ch *Channel) QueueInspect(name string) (Queue, error) {
+ req := &queueDeclare{
+ Queue: name,
+ Passive: true,
+ }
+ res := &queueDeclareOk{}
+
+ err := ch.call(req, res)
+
+ state := Queue{
+ Name: name,
+ Messages: int(res.MessageCount),
+ Consumers: int(res.ConsumerCount),
+ }
+
+ return state, err
+}
+
+/*
+QueueBind binds an exchange to a queue so that publishings to the exchange will
+be routed to the queue when the publishing routing key matches the binding
+routing key.
+
+ QueueBind("pagers", "alert", "log", false, nil)
+ QueueBind("emails", "info", "log", false, nil)
+
+ Delivery Exchange Key Queue
+ -----------------------------------------------
+ key: alert --> log ----> alert --> pagers
+ key: info ---> log ----> info ---> emails
+ key: debug --> log (none) (dropped)
+
+If a binding with the same key and arguments already exists between the
+exchange and queue, the attempt to rebind will be ignored and the existing
+binding will be retained.
+
+In the case that multiple bindings may cause the message to be routed to the
+same queue, the server will only route the publishing once. This is possible
+with topic exchanges.
+
+ QueueBind("pagers", "alert", "amq.topic", false, nil)
+ QueueBind("emails", "info", "amq.topic", false, nil)
+ QueueBind("emails", "#", "amq.topic", false, nil) // match everything
+
+ Delivery Exchange Key Queue
+ -----------------------------------------------
+ key: alert --> amq.topic ----> alert --> pagers
+ key: info ---> amq.topic ----> # ------> emails
+ \---> info ---/
+ key: debug --> amq.topic ----> # ------> emails
+
+It is only possible to bind a durable queue to a durable exchange regardless of
+whether the queue or exchange is auto-deleted. Bindings between durable queues
+and exchanges will also be restored on server restart.
+
+If the binding could not complete, an error will be returned and the channel
+will be closed.
+
+When noWait is true and the queue could not be bound, the channel will be
+closed with an error.
+
+*/
+func (ch *Channel) QueueBind(name, key, exchange string, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &queueBind{
+ Queue: name,
+ Exchange: exchange,
+ RoutingKey: key,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &queueBindOk{},
+ )
+}
+
+/*
+QueueUnbind removes a binding between an exchange and queue matching the key and
+arguments.
+
+It is possible to send and empty string for the exchange name which means to
+unbind the queue from the default exchange.
+
+*/
+func (ch *Channel) QueueUnbind(name, key, exchange string, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &queueUnbind{
+ Queue: name,
+ Exchange: exchange,
+ RoutingKey: key,
+ Arguments: args,
+ },
+ &queueUnbindOk{},
+ )
+}
+
+/*
+QueuePurge removes all messages from the named queue which are not waiting to
+be acknowledged. Messages that have been delivered but have not yet been
+acknowledged will not be removed.
+
+When successful, returns the number of messages purged.
+
+If noWait is true, do not wait for the server response and the number of
+messages purged will not be meaningful.
+*/
+func (ch *Channel) QueuePurge(name string, noWait bool) (int, error) {
+ req := &queuePurge{
+ Queue: name,
+ NoWait: noWait,
+ }
+ res := &queuePurgeOk{}
+
+ err := ch.call(req, res)
+
+ return int(res.MessageCount), err
+}
+
+/*
+QueueDelete removes the queue from the server including all bindings then
+purges the messages based on server configuration, returning the number of
+messages purged.
+
+When ifUnused is true, the queue will not be deleted if there are any
+consumers on the queue. If there are consumers, an error will be returned and
+the channel will be closed.
+
+When ifEmpty is true, the queue will not be deleted if there are any messages
+remaining on the queue. If there are messages, an error will be returned and
+the channel will be closed.
+
+When noWait is true, the queue will be deleted without waiting for a response
+from the server. The purged message count will not be meaningful. If the queue
+could not be deleted, a channel exception will be raised and the channel will
+be closed.
+
+*/
+func (ch *Channel) QueueDelete(name string, ifUnused, ifEmpty, noWait bool) (int, error) {
+ req := &queueDelete{
+ Queue: name,
+ IfUnused: ifUnused,
+ IfEmpty: ifEmpty,
+ NoWait: noWait,
+ }
+ res := &queueDeleteOk{}
+
+ err := ch.call(req, res)
+
+ return int(res.MessageCount), err
+}
+
+/*
+Consume immediately starts delivering queued messages.
+
+Begin receiving on the returned chan Delivery before any other operation on the
+Connection or Channel.
+
+Continues deliveries to the returned chan Delivery until Channel.Cancel,
+Connection.Close, Channel.Close, or an AMQP exception occurs. Consumers must
+range over the chan to ensure all deliveries are received. Unreceived
+deliveries will block all methods on the same connection.
+
+All deliveries in AMQP must be acknowledged. It is expected of the consumer to
+call Delivery.Ack after it has successfully processed the delivery. If the
+consumer is cancelled or the channel or connection is closed any unacknowledged
+deliveries will be requeued at the end of the same queue.
+
+The consumer is identified by a string that is unique and scoped for all
+consumers on this channel. If you wish to eventually cancel the consumer, use
+the same non-empty identifier in Channel.Cancel. An empty string will cause
+the library to generate a unique identity. The consumer identity will be
+included in every Delivery in the ConsumerTag field
+
+When autoAck (also known as noAck) is true, the server will acknowledge
+deliveries to this consumer prior to writing the delivery to the network. When
+autoAck is true, the consumer should not call Delivery.Ack. Automatically
+acknowledging deliveries means that some deliveries may get lost if the
+consumer is unable to process them after the server delivers them.
+
+When exclusive is true, the server will ensure that this is the sole consumer
+from this queue. When exclusive is false, the server will fairly distribute
+deliveries across multiple consumers.
+
+When noLocal is true, the server will not deliver publishing sent from the same
+connection to this consumer. It's advisable to use separate connections for
+Channel.Publish and Channel.Consume so not to have TCP pushback on publishing
+affect the ability to consume messages, so this parameter is here mostly for
+completeness.
+
+When noWait is true, do not wait for the server to confirm the request and
+immediately begin deliveries. If it is not possible to consume, a channel
+exception will be raised and the channel will be closed.
+
+Optional arguments can be provided that have specific semantics for the queue
+or server.
+
+When the channel or connection closes, all delivery chans will also close.
+
+Deliveries on the returned chan will be buffered indefinitely. To limit memory
+of this buffer, use the Channel.Qos method to limit the amount of
+unacknowledged/buffered deliveries the server will deliver on this Channel.
+
+*/
+func (ch *Channel) Consume(queue, consumer string, autoAck, exclusive, noLocal, noWait bool, args Table) (<-chan Delivery, error) {
+ // When we return from ch.call, there may be a delivery already for the
+ // consumer that hasn't been added to the consumer hash yet. Because of
+ // this, we never rely on the server picking a consumer tag for us.
+
+ if err := args.Validate(); err != nil {
+ return nil, err
+ }
+
+ if consumer == "" {
+ consumer = uniqueConsumerTag()
+ }
+
+ req := &basicConsume{
+ Queue: queue,
+ ConsumerTag: consumer,
+ NoLocal: noLocal,
+ NoAck: autoAck,
+ Exclusive: exclusive,
+ NoWait: noWait,
+ Arguments: args,
+ }
+ res := &basicConsumeOk{}
+
+ deliveries := make(chan Delivery)
+
+ ch.consumers.add(consumer, deliveries)
+
+ if err := ch.call(req, res); err != nil {
+ ch.consumers.close(consumer)
+ return nil, err
+ }
+
+ return (<-chan Delivery)(deliveries), nil
+}
+
+/*
+ExchangeDeclare declares an exchange on the server. If the exchange does not
+already exist, the server will create it. If the exchange exists, the server
+verifies that it is of the provided type, durability and auto-delete flags.
+
+Errors returned from this method will close the channel.
+
+Exchange names starting with "amq." are reserved for pre-declared and
+standardized exchanges. The client MAY declare an exchange starting with
+"amq." if the passive option is set, or the exchange already exists. Names can
+consist of a non-empty sequence of letters, digits, hyphen, underscore,
+period, or colon.
+
+Each exchange belongs to one of a set of exchange kinds/types implemented by
+the server. The exchange types define the functionality of the exchange - i.e.
+how messages are routed through it. Once an exchange is declared, its type
+cannot be changed. The common types are "direct", "fanout", "topic" and
+"headers".
+
+Durable and Non-Auto-Deleted exchanges will survive server restarts and remain
+declared when there are no remaining bindings. This is the best lifetime for
+long-lived exchange configurations like stable routes and default exchanges.
+
+Non-Durable and Auto-Deleted exchanges will be deleted when there are no
+remaining bindings and not restored on server restart. This lifetime is
+useful for temporary topologies that should not pollute the virtual host on
+failure or after the consumers have completed.
+
+Non-Durable and Non-Auto-deleted exchanges will remain as long as the server is
+running including when there are no remaining bindings. This is useful for
+temporary topologies that may have long delays between bindings.
+
+Durable and Auto-Deleted exchanges will survive server restarts and will be
+removed before and after server restarts when there are no remaining bindings.
+These exchanges are useful for robust temporary topologies or when you require
+binding durable queues to auto-deleted exchanges.
+
+Note: RabbitMQ declares the default exchange types like 'amq.fanout' as
+durable, so queues that bind to these pre-declared exchanges must also be
+durable.
+
+Exchanges declared as `internal` do not accept accept publishings. Internal
+exchanges are useful when you wish to implement inter-exchange topologies
+that should not be exposed to users of the broker.
+
+When noWait is true, declare without waiting for a confirmation from the server.
+The channel may be closed as a result of an error. Add a NotifyClose listener
+to respond to any exceptions.
+
+Optional amqp.Table of arguments that are specific to the server's implementation of
+the exchange can be sent for exchange types that require extra parameters.
+*/
+func (ch *Channel) ExchangeDeclare(name, kind string, durable, autoDelete, internal, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &exchangeDeclare{
+ Exchange: name,
+ Type: kind,
+ Passive: false,
+ Durable: durable,
+ AutoDelete: autoDelete,
+ Internal: internal,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &exchangeDeclareOk{},
+ )
+}
+
+/*
+
+ExchangeDeclarePassive is functionally and parametrically equivalent to
+ExchangeDeclare, except that it sets the "passive" attribute to true. A passive
+exchange is assumed by RabbitMQ to already exist, and attempting to connect to a
+non-existent exchange will cause RabbitMQ to throw an exception. This function
+can be used to detect the existence of an exchange.
+
+*/
+func (ch *Channel) ExchangeDeclarePassive(name, kind string, durable, autoDelete, internal, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &exchangeDeclare{
+ Exchange: name,
+ Type: kind,
+ Passive: true,
+ Durable: durable,
+ AutoDelete: autoDelete,
+ Internal: internal,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &exchangeDeclareOk{},
+ )
+}
+
+/*
+ExchangeDelete removes the named exchange from the server. When an exchange is
+deleted all queue bindings on the exchange are also deleted. If this exchange
+does not exist, the channel will be closed with an error.
+
+When ifUnused is true, the server will only delete the exchange if it has no queue
+bindings. If the exchange has queue bindings the server does not delete it
+but close the channel with an exception instead. Set this to true if you are
+not the sole owner of the exchange.
+
+When noWait is true, do not wait for a server confirmation that the exchange has
+been deleted. Failing to delete the channel could close the channel. Add a
+NotifyClose listener to respond to these channel exceptions.
+*/
+func (ch *Channel) ExchangeDelete(name string, ifUnused, noWait bool) error {
+ return ch.call(
+ &exchangeDelete{
+ Exchange: name,
+ IfUnused: ifUnused,
+ NoWait: noWait,
+ },
+ &exchangeDeleteOk{},
+ )
+}
+
+/*
+ExchangeBind binds an exchange to another exchange to create inter-exchange
+routing topologies on the server. This can decouple the private topology and
+routing exchanges from exchanges intended solely for publishing endpoints.
+
+Binding two exchanges with identical arguments will not create duplicate
+bindings.
+
+Binding one exchange to another with multiple bindings will only deliver a
+message once. For example if you bind your exchange to `amq.fanout` with two
+different binding keys, only a single message will be delivered to your
+exchange even though multiple bindings will match.
+
+Given a message delivered to the source exchange, the message will be forwarded
+to the destination exchange when the routing key is matched.
+
+ ExchangeBind("sell", "MSFT", "trade", false, nil)
+ ExchangeBind("buy", "AAPL", "trade", false, nil)
+
+ Delivery Source Key Destination
+ example exchange exchange
+ -----------------------------------------------
+ key: AAPL --> trade ----> MSFT sell
+ \---> AAPL --> buy
+
+When noWait is true, do not wait for the server to confirm the binding. If any
+error occurs the channel will be closed. Add a listener to NotifyClose to
+handle these errors.
+
+Optional arguments specific to the exchanges bound can also be specified.
+*/
+func (ch *Channel) ExchangeBind(destination, key, source string, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &exchangeBind{
+ Destination: destination,
+ Source: source,
+ RoutingKey: key,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &exchangeBindOk{},
+ )
+}
+
+/*
+ExchangeUnbind unbinds the destination exchange from the source exchange on the
+server by removing the routing key between them. This is the inverse of
+ExchangeBind. If the binding does not currently exist, an error will be
+returned.
+
+When noWait is true, do not wait for the server to confirm the deletion of the
+binding. If any error occurs the channel will be closed. Add a listener to
+NotifyClose to handle these errors.
+
+Optional arguments that are specific to the type of exchanges bound can also be
+provided. These must match the same arguments specified in ExchangeBind to
+identify the binding.
+*/
+func (ch *Channel) ExchangeUnbind(destination, key, source string, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &exchangeUnbind{
+ Destination: destination,
+ Source: source,
+ RoutingKey: key,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &exchangeUnbindOk{},
+ )
+}
+
+/*
+Publish sends a Publishing from the client to an exchange on the server.
+
+When you want a single message to be delivered to a single queue, you can
+publish to the default exchange with the routingKey of the queue name. This is
+because every declared queue gets an implicit route to the default exchange.
+
+Since publishings are asynchronous, any undeliverable message will get returned
+by the server. Add a listener with Channel.NotifyReturn to handle any
+undeliverable message when calling publish with either the mandatory or
+immediate parameters as true.
+
+Publishings can be undeliverable when the mandatory flag is true and no queue is
+bound that matches the routing key, or when the immediate flag is true and no
+consumer on the matched queue is ready to accept the delivery.
+
+This can return an error when the channel, connection or socket is closed. The
+error or lack of an error does not indicate whether the server has received this
+publishing.
+
+It is possible for publishing to not reach the broker if the underlying socket
+is shut down without pending publishing packets being flushed from the kernel
+buffers. The easy way of making it probable that all publishings reach the
+server is to always call Connection.Close before terminating your publishing
+application. The way to ensure that all publishings reach the server is to add
+a listener to Channel.NotifyPublish and put the channel in confirm mode with
+Channel.Confirm. Publishing delivery tags and their corresponding
+confirmations start at 1. Exit when all publishings are confirmed.
+
+When Publish does not return an error and the channel is in confirm mode, the
+internal counter for DeliveryTags with the first confirmation starts at 1.
+
+*/
+func (ch *Channel) Publish(exchange, key string, mandatory, immediate bool, msg Publishing) error {
+ if err := msg.Headers.Validate(); err != nil {
+ return err
+ }
+
+ ch.m.Lock()
+ defer ch.m.Unlock()
+
+ if err := ch.send(ch, &basicPublish{
+ Exchange: exchange,
+ RoutingKey: key,
+ Mandatory: mandatory,
+ Immediate: immediate,
+ Body: msg.Body,
+ Properties: properties{
+ Headers: msg.Headers,
+ ContentType: msg.ContentType,
+ ContentEncoding: msg.ContentEncoding,
+ DeliveryMode: msg.DeliveryMode,
+ Priority: msg.Priority,
+ CorrelationId: msg.CorrelationId,
+ ReplyTo: msg.ReplyTo,
+ Expiration: msg.Expiration,
+ MessageId: msg.MessageId,
+ Timestamp: msg.Timestamp,
+ Type: msg.Type,
+ UserId: msg.UserId,
+ AppId: msg.AppId,
+ },
+ }); err != nil {
+ return err
+ }
+
+ if ch.confirming {
+ ch.confirms.Publish()
+ }
+
+ return nil
+}
+
+/*
+Get synchronously receives a single Delivery from the head of a queue from the
+server to the client. In almost all cases, using Channel.Consume will be
+preferred.
+
+If there was a delivery waiting on the queue and that delivery was received, the
+second return value will be true. If there was no delivery waiting or an error
+occurred, the ok bool will be false.
+
+All deliveries must be acknowledged including those from Channel.Get. Call
+Delivery.Ack on the returned delivery when you have fully processed this
+delivery.
+
+When autoAck is true, the server will automatically acknowledge this message so
+you don't have to. But if you are unable to fully process this message before
+the channel or connection is closed, the message will not get requeued.
+
+*/
+func (ch *Channel) Get(queue string, autoAck bool) (msg Delivery, ok bool, err error) {
+ req := &basicGet{Queue: queue, NoAck: autoAck}
+ res := &basicGetOk{}
+ empty := &basicGetEmpty{}
+
+ if err := ch.call(req, res, empty); err != nil {
+ return Delivery{}, false, err
+ }
+
+ if res.DeliveryTag > 0 {
+ return *(newDelivery(ch, res)), true, nil
+ }
+
+ return Delivery{}, false, nil
+}
+
+/*
+Tx puts the channel into transaction mode on the server. All publishings and
+acknowledgments following this method will be atomically committed or rolled
+back for a single queue. Call either Channel.TxCommit or Channel.TxRollback to
+leave a this transaction and immediately start a new transaction.
+
+The atomicity across multiple queues is not defined as queue declarations and
+bindings are not included in the transaction.
+
+The behavior of publishings that are delivered as mandatory or immediate while
+the channel is in a transaction is not defined.
+
+Once a channel has been put into transaction mode, it cannot be taken out of
+transaction mode. Use a different channel for non-transactional semantics.
+
+*/
+func (ch *Channel) Tx() error {
+ return ch.call(
+ &txSelect{},
+ &txSelectOk{},
+ )
+}
+
+/*
+TxCommit atomically commits all publishings and acknowledgments for a single
+queue and immediately start a new transaction.
+
+Calling this method without having called Channel.Tx is an error.
+
+*/
+func (ch *Channel) TxCommit() error {
+ return ch.call(
+ &txCommit{},
+ &txCommitOk{},
+ )
+}
+
+/*
+TxRollback atomically rolls back all publishings and acknowledgments for a
+single queue and immediately start a new transaction.
+
+Calling this method without having called Channel.Tx is an error.
+
+*/
+func (ch *Channel) TxRollback() error {
+ return ch.call(
+ &txRollback{},
+ &txRollbackOk{},
+ )
+}
+
+/*
+Flow pauses the delivery of messages to consumers on this channel. Channels
+are opened with flow control not active, to open a channel with paused
+deliveries immediately call this method with true after calling
+Connection.Channel.
+
+When active is true, this method asks the server to temporarily pause deliveries
+until called again with active as false.
+
+Channel.Get methods will not be affected by flow control.
+
+This method is not intended to act as window control. Use Channel.Qos to limit
+the number of unacknowledged messages or bytes in flight instead.
+
+The server may also send us flow methods to throttle our publishings. A well
+behaving publishing client should add a listener with Channel.NotifyFlow and
+pause its publishings when true is sent on that channel.
+
+Note: RabbitMQ prefers to use TCP push back to control flow for all channels on
+a connection, so under high volume scenarios, it's wise to open separate
+Connections for publishings and deliveries.
+
+*/
+func (ch *Channel) Flow(active bool) error {
+ return ch.call(
+ &channelFlow{Active: active},
+ &channelFlowOk{},
+ )
+}
+
+/*
+Confirm puts this channel into confirm mode so that the client can ensure all
+publishings have successfully been received by the server. After entering this
+mode, the server will send a basic.ack or basic.nack message with the deliver
+tag set to a 1 based incremental index corresponding to every publishing
+received after the this method returns.
+
+Add a listener to Channel.NotifyPublish to respond to the Confirmations. If
+Channel.NotifyPublish is not called, the Confirmations will be silently
+ignored.
+
+The order of acknowledgments is not bound to the order of deliveries.
+
+Ack and Nack confirmations will arrive at some point in the future.
+
+Unroutable mandatory or immediate messages are acknowledged immediately after
+any Channel.NotifyReturn listeners have been notified. Other messages are
+acknowledged when all queues that should have the message routed to them have
+either received acknowledgment of delivery or have enqueued the message,
+persisting the message if necessary.
+
+When noWait is true, the client will not wait for a response. A channel
+exception could occur if the server does not support this method.
+
+*/
+func (ch *Channel) Confirm(noWait bool) error {
+ if err := ch.call(
+ &confirmSelect{Nowait: noWait},
+ &confirmSelectOk{},
+ ); err != nil {
+ return err
+ }
+
+ ch.confirmM.Lock()
+ ch.confirming = true
+ ch.confirmM.Unlock()
+
+ return nil
+}
+
+/*
+Recover redelivers all unacknowledged deliveries on this channel.
+
+When requeue is false, messages will be redelivered to the original consumer.
+
+When requeue is true, messages will be redelivered to any available consumer,
+potentially including the original.
+
+If the deliveries cannot be recovered, an error will be returned and the channel
+will be closed.
+
+Note: this method is not implemented on RabbitMQ, use Delivery.Nack instead
+*/
+func (ch *Channel) Recover(requeue bool) error {
+ return ch.call(
+ &basicRecover{Requeue: requeue},
+ &basicRecoverOk{},
+ )
+}
+
+/*
+Ack acknowledges a delivery by its delivery tag when having been consumed with
+Channel.Consume or Channel.Get.
+
+Ack acknowledges all message received prior to the delivery tag when multiple
+is true.
+
+See also Delivery.Ack
+*/
+func (ch *Channel) Ack(tag uint64, multiple bool) error {
+ return ch.send(ch, &basicAck{
+ DeliveryTag: tag,
+ Multiple: multiple,
+ })
+}
+
+/*
+Nack negatively acknowledges a delivery by its delivery tag. Prefer this
+method to notify the server that you were not able to process this delivery and
+it must be redelivered or dropped.
+
+See also Delivery.Nack
+*/
+func (ch *Channel) Nack(tag uint64, multiple bool, requeue bool) error {
+ return ch.send(ch, &basicNack{
+ DeliveryTag: tag,
+ Multiple: multiple,
+ Requeue: requeue,
+ })
+}
+
+/*
+Reject negatively acknowledges a delivery by its delivery tag. Prefer Nack
+over Reject when communicating with a RabbitMQ server because you can Nack
+multiple messages, reducing the amount of protocol messages to exchange.
+
+See also Delivery.Reject
+*/
+func (ch *Channel) Reject(tag uint64, requeue bool) error {
+ return ch.send(ch, &basicReject{
+ DeliveryTag: tag,
+ Requeue: requeue,
+ })
+}
diff --git a/vendor/github.com/streadway/amqp/client_test.go b/vendor/github.com/streadway/amqp/client_test.go
new file mode 100644
index 00000000..9497c236
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/client_test.go
@@ -0,0 +1,601 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "bytes"
+ "io"
+ "reflect"
+ "testing"
+ "time"
+)
+
+type server struct {
+ *testing.T
+ r reader // framer <- client
+ w writer // framer -> client
+ S io.ReadWriteCloser // Server IO
+ C io.ReadWriteCloser // Client IO
+
+ // captured client frames
+ start connectionStartOk
+ tune connectionTuneOk
+}
+
+func defaultConfig() Config {
+ return Config{SASL: []Authentication{&PlainAuth{"guest", "guest"}}, Vhost: "/"}
+}
+
+func newSession(t *testing.T) (io.ReadWriteCloser, *server) {
+ rs, wc := io.Pipe()
+ rc, ws := io.Pipe()
+
+ rws := &logIO{t, "server", pipe{rs, ws}}
+ rwc := &logIO{t, "client", pipe{rc, wc}}
+
+ server := server{
+ T: t,
+ r: reader{rws},
+ w: writer{rws},
+ S: rws,
+ C: rwc,
+ }
+
+ return rwc, &server
+}
+
+func (t *server) expectBytes(b []byte) {
+ in := make([]byte, len(b))
+ if _, err := io.ReadFull(t.S, in); err != nil {
+ t.Fatalf("io error expecting bytes: %v", err)
+ }
+
+ if bytes.Compare(b, in) != 0 {
+ t.Fatalf("failed bytes: expected: %s got: %s", string(b), string(in))
+ }
+}
+
+func (t *server) send(channel int, m message) {
+ defer time.AfterFunc(time.Second, func() { panic("send deadlock") }).Stop()
+
+ if err := t.w.WriteFrame(&methodFrame{
+ ChannelId: uint16(channel),
+ Method: m,
+ }); err != nil {
+ t.Fatalf("frame err, write: %s", err)
+ }
+}
+
+// drops all but method frames expected on the given channel
+func (t *server) recv(channel int, m message) message {
+ defer time.AfterFunc(time.Second, func() { panic("recv deadlock") }).Stop()
+
+ var remaining int
+ var header *headerFrame
+ var body []byte
+
+ for {
+ frame, err := t.r.ReadFrame()
+ if err != nil {
+ t.Fatalf("frame err, read: %s", err)
+ }
+
+ if frame.channel() != uint16(channel) {
+ t.Fatalf("expected frame on channel %d, got channel %d", channel, frame.channel())
+ }
+
+ switch f := frame.(type) {
+ case *heartbeatFrame:
+ // drop
+
+ case *headerFrame:
+ // start content state
+ header = f
+ remaining = int(header.Size)
+ if remaining == 0 {
+ m.(messageWithContent).setContent(header.Properties, nil)
+ return m
+ }
+
+ case *bodyFrame:
+ // continue until terminated
+ body = append(body, f.Body...)
+ remaining -= len(f.Body)
+ if remaining <= 0 {
+ m.(messageWithContent).setContent(header.Properties, body)
+ return m
+ }
+
+ case *methodFrame:
+ if reflect.TypeOf(m) == reflect.TypeOf(f.Method) {
+ wantv := reflect.ValueOf(m).Elem()
+ havev := reflect.ValueOf(f.Method).Elem()
+ wantv.Set(havev)
+ if _, ok := m.(messageWithContent); !ok {
+ return m
+ }
+ } else {
+ t.Fatalf("expected method type: %T, got: %T", m, f.Method)
+ }
+
+ default:
+ t.Fatalf("unexpected frame: %+v", f)
+ }
+ }
+}
+
+func (t *server) expectAMQP() {
+ t.expectBytes([]byte{'A', 'M', 'Q', 'P', 0, 0, 9, 1})
+}
+
+func (t *server) connectionStart() {
+ t.send(0, &connectionStart{
+ VersionMajor: 0,
+ VersionMinor: 9,
+ Mechanisms: "PLAIN",
+ Locales: "en-us",
+ })
+
+ t.recv(0, &t.start)
+}
+
+func (t *server) connectionTune() {
+ t.send(0, &connectionTune{
+ ChannelMax: 11,
+ FrameMax: 20000,
+ Heartbeat: 10,
+ })
+
+ t.recv(0, &t.tune)
+}
+
+func (t *server) connectionOpen() {
+ t.expectAMQP()
+ t.connectionStart()
+ t.connectionTune()
+
+ t.recv(0, &connectionOpen{})
+ t.send(0, &connectionOpenOk{})
+}
+
+func (t *server) connectionClose() {
+ t.recv(0, &connectionClose{})
+ t.send(0, &connectionCloseOk{})
+}
+
+func (t *server) channelOpen(id int) {
+ t.recv(id, &channelOpen{})
+ t.send(id, &channelOpenOk{})
+}
+
+func TestDefaultClientProperties(t *testing.T) {
+ rwc, srv := newSession(t)
+
+ go func() {
+ srv.connectionOpen()
+ rwc.Close()
+ }()
+
+ if c, err := Open(rwc, defaultConfig()); err != nil {
+ t.Fatalf("could not create connection: %v (%s)", c, err)
+ }
+
+ if want, got := defaultProduct, srv.start.ClientProperties["product"]; want != got {
+ t.Errorf("expected product %s got: %s", want, got)
+ }
+
+ if want, got := defaultVersion, srv.start.ClientProperties["version"]; want != got {
+ t.Errorf("expected version %s got: %s", want, got)
+ }
+}
+
+func TestCustomClientProperties(t *testing.T) {
+ rwc, srv := newSession(t)
+
+ config := defaultConfig()
+ config.Properties = Table{
+ "product": "foo",
+ "version": "1.0",
+ }
+
+ go func() {
+ srv.connectionOpen()
+ rwc.Close()
+ }()
+
+ if c, err := Open(rwc, config); err != nil {
+ t.Fatalf("could not create connection: %v (%s)", c, err)
+ }
+
+ if want, got := config.Properties["product"], srv.start.ClientProperties["product"]; want != got {
+ t.Errorf("expected product %s got: %s", want, got)
+ }
+
+ if want, got := config.Properties["version"], srv.start.ClientProperties["version"]; want != got {
+ t.Errorf("expected version %s got: %s", want, got)
+ }
+}
+
+func TestOpen(t *testing.T) {
+ rwc, srv := newSession(t)
+ go func() {
+ srv.connectionOpen()
+ rwc.Close()
+ }()
+
+ if c, err := Open(rwc, defaultConfig()); err != nil {
+ t.Fatalf("could not create connection: %v (%s)", c, err)
+ }
+}
+
+func TestChannelOpen(t *testing.T) {
+ rwc, srv := newSession(t)
+
+ go func() {
+ srv.connectionOpen()
+ srv.channelOpen(1)
+
+ rwc.Close()
+ }()
+
+ c, err := Open(rwc, defaultConfig())
+ if err != nil {
+ t.Fatalf("could not create connection: %v (%s)", c, err)
+ }
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("could not open channel: %v (%s)", ch, err)
+ }
+}
+
+func TestOpenFailedSASLUnsupportedMechanisms(t *testing.T) {
+ rwc, srv := newSession(t)
+
+ go func() {
+ srv.expectAMQP()
+ srv.send(0, &connectionStart{
+ VersionMajor: 0,
+ VersionMinor: 9,
+ Mechanisms: "KERBEROS NTLM",
+ Locales: "en-us",
+ })
+ }()
+
+ c, err := Open(rwc, defaultConfig())
+ if err != ErrSASL {
+ t.Fatalf("expected ErrSASL got: %+v on %+v", err, c)
+ }
+}
+
+func TestOpenFailedCredentials(t *testing.T) {
+ rwc, srv := newSession(t)
+
+ go func() {
+ srv.expectAMQP()
+ srv.connectionStart()
+ // Now kill/timeout the connection indicating bad auth
+ rwc.Close()
+ }()
+
+ c, err := Open(rwc, defaultConfig())
+ if err != ErrCredentials {
+ t.Fatalf("expected ErrCredentials got: %+v on %+v", err, c)
+ }
+}
+
+func TestOpenFailedVhost(t *testing.T) {
+ rwc, srv := newSession(t)
+
+ go func() {
+ srv.expectAMQP()
+ srv.connectionStart()
+ srv.connectionTune()
+ srv.recv(0, &connectionOpen{})
+
+ // Now kill/timeout the connection on bad Vhost
+ rwc.Close()
+ }()
+
+ c, err := Open(rwc, defaultConfig())
+ if err != ErrVhost {
+ t.Fatalf("expected ErrVhost got: %+v on %+v", err, c)
+ }
+}
+
+func TestConfirmMultipleOrdersDeliveryTags(t *testing.T) {
+ rwc, srv := newSession(t)
+ defer rwc.Close()
+
+ go func() {
+ srv.connectionOpen()
+ srv.channelOpen(1)
+
+ srv.recv(1, &confirmSelect{})
+ srv.send(1, &confirmSelectOk{})
+
+ srv.recv(1, &basicPublish{})
+ srv.recv(1, &basicPublish{})
+ srv.recv(1, &basicPublish{})
+ srv.recv(1, &basicPublish{})
+
+ // Single tag, plus multiple, should produce
+ // 2, 1, 3, 4
+ srv.send(1, &basicAck{DeliveryTag: 2})
+ srv.send(1, &basicAck{DeliveryTag: 1})
+ srv.send(1, &basicAck{DeliveryTag: 4, Multiple: true})
+
+ srv.recv(1, &basicPublish{})
+ srv.recv(1, &basicPublish{})
+ srv.recv(1, &basicPublish{})
+ srv.recv(1, &basicPublish{})
+
+ // And some more, but in reverse order, multiple then one
+ // 5, 6, 7, 8
+ srv.send(1, &basicAck{DeliveryTag: 6, Multiple: true})
+ srv.send(1, &basicAck{DeliveryTag: 8})
+ srv.send(1, &basicAck{DeliveryTag: 7})
+ }()
+
+ c, err := Open(rwc, defaultConfig())
+ if err != nil {
+ t.Fatalf("could not create connection: %v (%s)", c, err)
+ }
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("could not open channel: %v (%s)", ch, err)
+ }
+
+ confirm := ch.NotifyPublish(make(chan Confirmation))
+
+ ch.Confirm(false)
+
+ go func() {
+ ch.Publish("", "q", false, false, Publishing{Body: []byte("pub 1")})
+ ch.Publish("", "q", false, false, Publishing{Body: []byte("pub 2")})
+ ch.Publish("", "q", false, false, Publishing{Body: []byte("pub 3")})
+ ch.Publish("", "q", false, false, Publishing{Body: []byte("pub 4")})
+ }()
+
+ // received out of order, consumed in order
+ for i, tag := range []uint64{1, 2, 3, 4} {
+ if ack := <-confirm; tag != ack.DeliveryTag {
+ t.Fatalf("failed ack, expected ack#%d to be %d, got %d", i, tag, ack.DeliveryTag)
+ }
+ }
+
+ go func() {
+ ch.Publish("", "q", false, false, Publishing{Body: []byte("pub 5")})
+ ch.Publish("", "q", false, false, Publishing{Body: []byte("pub 6")})
+ ch.Publish("", "q", false, false, Publishing{Body: []byte("pub 7")})
+ ch.Publish("", "q", false, false, Publishing{Body: []byte("pub 8")})
+ }()
+
+ for i, tag := range []uint64{5, 6, 7, 8} {
+ if ack := <-confirm; tag != ack.DeliveryTag {
+ t.Fatalf("failed ack, expected ack#%d to be %d, got %d", i, tag, ack.DeliveryTag)
+ }
+ }
+
+}
+
+func TestNotifyClosesReusedPublisherConfirmChan(t *testing.T) {
+ rwc, srv := newSession(t)
+
+ go func() {
+ srv.connectionOpen()
+ srv.channelOpen(1)
+
+ srv.recv(1, &confirmSelect{})
+ srv.send(1, &confirmSelectOk{})
+
+ srv.recv(0, &connectionClose{})
+ srv.send(0, &connectionCloseOk{})
+ }()
+
+ c, err := Open(rwc, defaultConfig())
+ if err != nil {
+ t.Fatalf("could not create connection: %v (%s)", c, err)
+ }
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("could not open channel: %v (%s)", ch, err)
+ }
+
+ ackAndNack := make(chan uint64)
+ ch.NotifyConfirm(ackAndNack, ackAndNack)
+
+ if err := ch.Confirm(false); err != nil {
+ t.Fatalf("expected to enter confirm mode: %v", err)
+ }
+
+ if err := c.Close(); err != nil {
+ t.Fatalf("could not close connection: %v (%s)", c, err)
+ }
+}
+
+func TestNotifyClosesAllChansAfterConnectionClose(t *testing.T) {
+ rwc, srv := newSession(t)
+
+ go func() {
+ srv.connectionOpen()
+ srv.channelOpen(1)
+
+ srv.recv(0, &connectionClose{})
+ srv.send(0, &connectionCloseOk{})
+ }()
+
+ c, err := Open(rwc, defaultConfig())
+ if err != nil {
+ t.Fatalf("could not create connection: %v (%s)", c, err)
+ }
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("could not open channel: %v (%s)", ch, err)
+ }
+
+ if err := c.Close(); err != nil {
+ t.Fatalf("could not close connection: %v (%s)", c, err)
+ }
+
+ select {
+ case <-c.NotifyClose(make(chan *Error)):
+ case <-time.After(time.Millisecond):
+ t.Errorf("expected to close NotifyClose chan after Connection.Close")
+ }
+
+ select {
+ case <-ch.NotifyClose(make(chan *Error)):
+ case <-time.After(time.Millisecond):
+ t.Errorf("expected to close Connection.NotifyClose chan after Connection.Close")
+ }
+
+ select {
+ case <-ch.NotifyFlow(make(chan bool)):
+ case <-time.After(time.Millisecond):
+ t.Errorf("expected to close Channel.NotifyFlow chan after Connection.Close")
+ }
+
+ select {
+ case <-ch.NotifyCancel(make(chan string)):
+ case <-time.After(time.Millisecond):
+ t.Errorf("expected to close Channel.NofityCancel chan after Connection.Close")
+ }
+
+ select {
+ case <-ch.NotifyReturn(make(chan Return)):
+ case <-time.After(time.Millisecond):
+ t.Errorf("expected to close Channel.NotifyReturn chan after Connection.Close")
+ }
+
+ confirms := ch.NotifyPublish(make(chan Confirmation))
+
+ select {
+ case <-confirms:
+ case <-time.After(time.Millisecond):
+ t.Errorf("expected to close confirms on Channel.NotifyPublish chan after Connection.Close")
+ }
+}
+
+// Should not panic when sending bodies split at different boundaries
+func TestPublishBodySliceIssue74(t *testing.T) {
+ rwc, srv := newSession(t)
+ defer rwc.Close()
+
+ const frameSize = 100
+ const publishings = frameSize * 3
+
+ done := make(chan bool)
+ base := make([]byte, publishings)
+
+ go func() {
+ srv.connectionOpen()
+ srv.channelOpen(1)
+
+ for i := 0; i < publishings; i++ {
+ srv.recv(1, &basicPublish{})
+ }
+
+ done <- true
+ }()
+
+ cfg := defaultConfig()
+ cfg.FrameSize = frameSize
+
+ c, err := Open(rwc, cfg)
+ if err != nil {
+ t.Fatalf("could not create connection: %v (%s)", c, err)
+ }
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("could not open channel: %v (%s)", ch, err)
+ }
+
+ for i := 0; i < publishings; i++ {
+ go ch.Publish("", "q", false, false, Publishing{Body: base[0:i]})
+ }
+
+ <-done
+}
+
+// Should not panic when server and client have frame_size of 0
+func TestPublishZeroFrameSizeIssue161(t *testing.T) {
+ rwc, srv := newSession(t)
+ defer rwc.Close()
+
+ const frameSize = 0
+ const publishings = 1
+ done := make(chan bool)
+
+ go func() {
+ srv.connectionOpen()
+ srv.channelOpen(1)
+
+ for i := 0; i < publishings; i++ {
+ srv.recv(1, &basicPublish{})
+ }
+
+ done <- true
+ }()
+
+ cfg := defaultConfig()
+ cfg.FrameSize = frameSize
+
+ c, err := Open(rwc, cfg)
+
+ // override the tuned framesize with a hard 0, as would happen when rabbit is configured with 0
+ c.Config.FrameSize = frameSize
+
+ if err != nil {
+ t.Fatalf("could not create connection: %v (%s)", c, err)
+ }
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("could not open channel: %v (%s)", ch, err)
+ }
+
+ for i := 0; i < publishings; i++ {
+ go ch.Publish("", "q", false, false, Publishing{Body: []byte("anything")})
+ }
+
+ <-done
+}
+
+func TestPublishAndShutdownDeadlockIssue84(t *testing.T) {
+ rwc, srv := newSession(t)
+ defer rwc.Close()
+
+ go func() {
+ srv.connectionOpen()
+ srv.channelOpen(1)
+ srv.recv(1, &basicPublish{})
+ // Mimic a broken io pipe so that Publish catches the error and goes into shutdown
+ srv.S.Close()
+ }()
+
+ c, err := Open(rwc, defaultConfig())
+ if err != nil {
+ t.Fatalf("couldn't create connection: %v (%s)", c, err)
+ }
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("couldn't open channel: %v (%s)", ch, err)
+ }
+
+ defer time.AfterFunc(500*time.Millisecond, func() { panic("Publish deadlock") }).Stop()
+ for {
+ if err := ch.Publish("exchange", "q", false, false, Publishing{Body: []byte("test")}); err != nil {
+ t.Log("successfully caught disconnect error", err)
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/confirms.go b/vendor/github.com/streadway/amqp/confirms.go
new file mode 100644
index 00000000..06cbaa71
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/confirms.go
@@ -0,0 +1,94 @@
+package amqp
+
+import "sync"
+
+// confirms resequences and notifies one or multiple publisher confirmation listeners
+type confirms struct {
+ m sync.Mutex
+ listeners []chan Confirmation
+ sequencer map[uint64]Confirmation
+ published uint64
+ expecting uint64
+}
+
+// newConfirms allocates a confirms
+func newConfirms() *confirms {
+ return &confirms{
+ sequencer: map[uint64]Confirmation{},
+ published: 0,
+ expecting: 1,
+ }
+}
+
+func (c *confirms) Listen(l chan Confirmation) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ c.listeners = append(c.listeners, l)
+}
+
+// publish increments the publishing counter
+func (c *confirms) Publish() uint64 {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ c.published++
+ return c.published
+}
+
+// confirm confirms one publishing, increments the expecting delivery tag, and
+// removes bookkeeping for that delivery tag.
+func (c *confirms) confirm(confirmation Confirmation) {
+ delete(c.sequencer, c.expecting)
+ c.expecting++
+ for _, l := range c.listeners {
+ l <- confirmation
+ }
+}
+
+// resequence confirms any out of order delivered confirmations
+func (c *confirms) resequence() {
+ for c.expecting <= c.published {
+ sequenced, found := c.sequencer[c.expecting]
+ if !found {
+ return
+ }
+ c.confirm(sequenced)
+ }
+}
+
+// one confirms one publishing and all following in the publishing sequence
+func (c *confirms) One(confirmed Confirmation) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ if c.expecting == confirmed.DeliveryTag {
+ c.confirm(confirmed)
+ } else {
+ c.sequencer[confirmed.DeliveryTag] = confirmed
+ }
+ c.resequence()
+}
+
+// multiple confirms all publishings up until the delivery tag
+func (c *confirms) Multiple(confirmed Confirmation) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ for c.expecting <= confirmed.DeliveryTag {
+ c.confirm(Confirmation{c.expecting, confirmed.Ack})
+ }
+ c.resequence()
+}
+
+// Close closes all listeners, discarding any out of sequence confirmations
+func (c *confirms) Close() error {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ for _, l := range c.listeners {
+ close(l)
+ }
+ c.listeners = nil
+ return nil
+}
diff --git a/vendor/github.com/streadway/amqp/confirms_test.go b/vendor/github.com/streadway/amqp/confirms_test.go
new file mode 100644
index 00000000..03abd875
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/confirms_test.go
@@ -0,0 +1,153 @@
+package amqp
+
+import (
+ "testing"
+ "time"
+)
+
+func TestConfirmOneResequences(t *testing.T) {
+ var (
+ fixtures = []Confirmation{
+ {1, true},
+ {2, false},
+ {3, true},
+ }
+ c = newConfirms()
+ l = make(chan Confirmation, len(fixtures))
+ )
+
+ c.Listen(l)
+
+ for i := range fixtures {
+ if want, got := uint64(i+1), c.Publish(); want != got {
+ t.Fatalf("expected publish to return the 1 based delivery tag published, want: %d, got: %d", want, got)
+ }
+ }
+
+ c.One(fixtures[1])
+ c.One(fixtures[2])
+
+ select {
+ case confirm := <-l:
+ t.Fatalf("expected to wait in order to properly resequence results, got: %+v", confirm)
+ default:
+ }
+
+ c.One(fixtures[0])
+
+ for i, fix := range fixtures {
+ if want, got := fix, <-l; want != got {
+ t.Fatalf("expected to return confirmations in sequence for %d, want: %+v, got: %+v", i, want, got)
+ }
+ }
+}
+
+func TestConfirmMixedResequences(t *testing.T) {
+ var (
+ fixtures = []Confirmation{
+ {1, true},
+ {2, true},
+ {3, true},
+ }
+ c = newConfirms()
+ l = make(chan Confirmation, len(fixtures))
+ )
+ c.Listen(l)
+
+ for _ = range fixtures {
+ c.Publish()
+ }
+
+ c.One(fixtures[0])
+ c.One(fixtures[2])
+ c.Multiple(fixtures[1])
+
+ for i, fix := range fixtures {
+ want := fix
+ var got Confirmation
+ select {
+ case got = <-l:
+ case <-time.After(1 * time.Second):
+ t.Fatalf("timeout on reading confirmations")
+ }
+ if want != got {
+ t.Fatalf("expected to confirm in sequence for %d, want: %+v, got: %+v", i, want, got)
+ }
+ }
+}
+
+func TestConfirmMultipleResequences(t *testing.T) {
+ var (
+ fixtures = []Confirmation{
+ {1, true},
+ {2, true},
+ {3, true},
+ {4, true},
+ }
+ c = newConfirms()
+ l = make(chan Confirmation, len(fixtures))
+ )
+ c.Listen(l)
+
+ for _ = range fixtures {
+ c.Publish()
+ }
+
+ c.Multiple(fixtures[len(fixtures)-1])
+
+ for i, fix := range fixtures {
+ if want, got := fix, <-l; want != got {
+ t.Fatalf("expected to confirm multiple in sequence for %d, want: %+v, got: %+v", i, want, got)
+ }
+ }
+}
+
+func BenchmarkSequentialBufferedConfirms(t *testing.B) {
+ var (
+ c = newConfirms()
+ l = make(chan Confirmation, 10)
+ )
+
+ c.Listen(l)
+
+ for i := 0; i < t.N; i++ {
+ if i > cap(l)-1 {
+ <-l
+ }
+ c.One(Confirmation{c.Publish(), true})
+ }
+}
+
+func TestConfirmsIsThreadSafe(t *testing.T) {
+ const count = 1000
+ const timeout = 5 * time.Second
+ var (
+ c = newConfirms()
+ l = make(chan Confirmation)
+ pub = make(chan Confirmation)
+ done = make(chan Confirmation)
+ late = time.After(timeout)
+ )
+
+ c.Listen(l)
+
+ for i := 0; i < count; i++ {
+ go func() { pub <- Confirmation{c.Publish(), true} }()
+ }
+
+ for i := 0; i < count; i++ {
+ go func() { c.One(<-pub) }()
+ }
+
+ for i := 0; i < count; i++ {
+ go func() { done <- <-l }()
+ }
+
+ for i := 0; i < count; i++ {
+ select {
+ case <-done:
+ case <-late:
+ t.Fatalf("expected all publish/confirms to finish after %s", timeout)
+ }
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/connection.go b/vendor/github.com/streadway/amqp/connection.go
new file mode 100644
index 00000000..91b4644d
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/connection.go
@@ -0,0 +1,817 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "bufio"
+ "crypto/tls"
+ "io"
+ "net"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+const (
+ maxChannelMax = (2 << 15) - 1
+
+ defaultHeartbeat = 10 * time.Second
+ defaultConnectionTimeout = 30 * time.Second
+ defaultProduct = "https://github.com/streadway/amqp"
+ defaultVersion = "β"
+ defaultChannelMax = maxChannelMax
+)
+
+// Config is used in DialConfig and Open to specify the desired tuning
+// parameters used during a connection open handshake. The negotiated tuning
+// will be stored in the returned connection's Config field.
+type Config struct {
+ // The SASL mechanisms to try in the client request, and the successful
+ // mechanism used on the Connection object.
+ // If SASL is nil, PlainAuth from the URL is used.
+ SASL []Authentication
+
+ // Vhost specifies the namespace of permissions, exchanges, queues and
+ // bindings on the server. Dial sets this to the path parsed from the URL.
+ Vhost string
+
+ ChannelMax int // 0 max channels means 2^16 - 1
+ FrameSize int // 0 max bytes means unlimited
+ Heartbeat time.Duration // less than 1s uses the server's interval
+
+ // TLSClientConfig specifies the client configuration of the TLS connection
+ // when establishing a tls transport.
+ // If the URL uses an amqps scheme, then an empty tls.Config with the
+ // ServerName from the URL is used.
+ TLSClientConfig *tls.Config
+
+ // Properties is table of properties that the client advertises to the server.
+ // This is an optional setting - if the application does not set this,
+ // the underlying library will use a generic set of client properties.
+ Properties Table
+
+ // Dial returns a net.Conn prepared for a TLS handshake with TSLClientConfig,
+ // then an AMQP connection handshake.
+ // If Dial is nil, net.DialTimeout with a 30s connection and 30s deadline is
+ // used during TLS and AMQP handshaking.
+ Dial func(network, addr string) (net.Conn, error)
+}
+
+// Connection manages the serialization and deserialization of frames from IO
+// and dispatches the frames to the appropriate channel. All RPC methods and
+// asyncronous Publishing, Delivery, Ack, Nack and Return messages are
+// multiplexed on this channel. There must always be active receivers for
+// every asynchronous message on this connection.
+type Connection struct {
+ destructor sync.Once // shutdown once
+ sendM sync.Mutex // conn writer mutex
+ m sync.Mutex // struct field mutex
+
+ conn io.ReadWriteCloser
+
+ rpc chan message
+ writer *writer
+ sends chan time.Time // timestamps of each frame sent
+ deadlines chan readDeadliner // heartbeater updates read deadlines
+
+ allocator *allocator // id generator valid after openTune
+ channels map[uint16]*Channel
+
+ noNotify bool // true when we will never notify again
+ closes []chan *Error
+ blocks []chan Blocking
+
+ errors chan *Error
+
+ Config Config // The negotiated Config after connection.open
+
+ Major int // Server's major version
+ Minor int // Server's minor version
+ Properties Table // Server properties
+
+ closed int32 // Will be 1 if the connection is closed, 0 otherwise. Should only be accessed as atomic
+}
+
+type readDeadliner interface {
+ SetReadDeadline(time.Time) error
+}
+
+// defaultDial establishes a connection when config.Dial is not provided
+func defaultDial(network, addr string) (net.Conn, error) {
+ conn, err := net.DialTimeout(network, addr, defaultConnectionTimeout)
+ if err != nil {
+ return nil, err
+ }
+
+ // Heartbeating hasn't started yet, don't stall forever on a dead server.
+ // A deadline is set for TLS and AMQP handshaking. After AMQP is established,
+ // the deadline is cleared in openComplete.
+ if err := conn.SetDeadline(time.Now().Add(defaultConnectionTimeout)); err != nil {
+ return nil, err
+ }
+
+ return conn, nil
+}
+
+// Dial accepts a string in the AMQP URI format and returns a new Connection
+// over TCP using PlainAuth. Defaults to a server heartbeat interval of 10
+// seconds and sets the handshake deadline to 30 seconds. After handshake,
+// deadlines are cleared.
+//
+// Dial uses the zero value of tls.Config when it encounters an amqps://
+// scheme. It is equivalent to calling DialTLS(amqp, nil).
+func Dial(url string) (*Connection, error) {
+ return DialConfig(url, Config{
+ Heartbeat: defaultHeartbeat,
+ })
+}
+
+// DialTLS accepts a string in the AMQP URI format and returns a new Connection
+// over TCP using PlainAuth. Defaults to a server heartbeat interval of 10
+// seconds and sets the initial read deadline to 30 seconds.
+//
+// DialTLS uses the provided tls.Config when encountering an amqps:// scheme.
+func DialTLS(url string, amqps *tls.Config) (*Connection, error) {
+ return DialConfig(url, Config{
+ Heartbeat: defaultHeartbeat,
+ TLSClientConfig: amqps,
+ })
+}
+
+// DialConfig accepts a string in the AMQP URI format and a configuration for
+// the transport and connection setup, returning a new Connection. Defaults to
+// a server heartbeat interval of 10 seconds and sets the initial read deadline
+// to 30 seconds.
+func DialConfig(url string, config Config) (*Connection, error) {
+ var err error
+ var conn net.Conn
+
+ uri, err := ParseURI(url)
+ if err != nil {
+ return nil, err
+ }
+
+ if config.SASL == nil {
+ config.SASL = []Authentication{uri.PlainAuth()}
+ }
+
+ if config.Vhost == "" {
+ config.Vhost = uri.Vhost
+ }
+
+ addr := net.JoinHostPort(uri.Host, strconv.FormatInt(int64(uri.Port), 10))
+
+ dialer := config.Dial
+ if dialer == nil {
+ dialer = defaultDial
+ }
+
+ conn, err = dialer("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+
+ if uri.Scheme == "amqps" {
+ if config.TLSClientConfig == nil {
+ config.TLSClientConfig = new(tls.Config)
+ }
+
+ // If ServerName has not been specified in TLSClientConfig,
+ // set it to the URI host used for this connection.
+ if config.TLSClientConfig.ServerName == "" {
+ config.TLSClientConfig.ServerName = uri.Host
+ }
+
+ client := tls.Client(conn, config.TLSClientConfig)
+ if err := client.Handshake(); err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ conn = client
+ }
+
+ return Open(conn, config)
+}
+
+/*
+Open accepts an already established connection, or other io.ReadWriteCloser as
+a transport. Use this method if you have established a TLS connection or wish
+to use your own custom transport.
+
+*/
+func Open(conn io.ReadWriteCloser, config Config) (*Connection, error) {
+ c := &Connection{
+ conn: conn,
+ writer: &writer{bufio.NewWriter(conn)},
+ channels: make(map[uint16]*Channel),
+ rpc: make(chan message),
+ sends: make(chan time.Time),
+ errors: make(chan *Error, 1),
+ deadlines: make(chan readDeadliner, 1),
+ }
+ go c.reader(conn)
+ return c, c.open(config)
+}
+
+/*
+LocalAddr returns the local TCP peer address, or ":0" (the zero value of net.TCPAddr)
+as a fallback default value if the underlying transport does not support LocalAddr().
+*/
+func (c *Connection) LocalAddr() net.Addr {
+ if conn, ok := c.conn.(interface {
+ LocalAddr() net.Addr
+ }); ok {
+ return conn.LocalAddr()
+ }
+ return &net.TCPAddr{}
+}
+
+// ConnectionState returns basic TLS details of the underlying transport.
+// Returns a zero value when the underlying connection does not implement
+// ConnectionState() tls.ConnectionState.
+func (c *Connection) ConnectionState() tls.ConnectionState {
+ if conn, ok := c.conn.(interface {
+ ConnectionState() tls.ConnectionState
+ }); ok {
+ return conn.ConnectionState()
+ }
+ return tls.ConnectionState{}
+}
+
+/*
+NotifyClose registers a listener for close events either initiated by an error
+accompaning a connection.close method or by a normal shutdown.
+
+On normal shutdowns, the chan will be closed.
+
+To reconnect after a transport or protocol error, register a listener here and
+re-run your setup process.
+
+*/
+func (c *Connection) NotifyClose(receiver chan *Error) chan *Error {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ if c.noNotify {
+ close(receiver)
+ } else {
+ c.closes = append(c.closes, receiver)
+ }
+
+ return receiver
+}
+
+/*
+NotifyBlocked registers a listener for RabbitMQ specific TCP flow control
+method extensions connection.blocked and connection.unblocked. Flow control is
+active with a reason when Blocking.Blocked is true. When a Connection is
+blocked, all methods will block across all connections until server resources
+become free again.
+
+This optional extension is supported by the server when the
+"connection.blocked" server capability key is true.
+
+*/
+func (c *Connection) NotifyBlocked(receiver chan Blocking) chan Blocking {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ if c.noNotify {
+ close(receiver)
+ } else {
+ c.blocks = append(c.blocks, receiver)
+ }
+
+ return receiver
+}
+
+/*
+Close requests and waits for the response to close the AMQP connection.
+
+It's advisable to use this message when publishing to ensure all kernel buffers
+have been flushed on the server and client before exiting.
+
+An error indicates that server may not have received this request to close but
+the connection should be treated as closed regardless.
+
+After returning from this call, all resources associated with this connection,
+including the underlying io, Channels, Notify listeners and Channel consumers
+will also be closed.
+*/
+func (c *Connection) Close() error {
+ if c.isClosed() {
+ return ErrClosed
+ }
+
+ defer c.shutdown(nil)
+ return c.call(
+ &connectionClose{
+ ReplyCode: replySuccess,
+ ReplyText: "kthxbai",
+ },
+ &connectionCloseOk{},
+ )
+}
+
+func (c *Connection) closeWith(err *Error) error {
+ if c.isClosed() {
+ return ErrClosed
+ }
+
+ defer c.shutdown(err)
+ return c.call(
+ &connectionClose{
+ ReplyCode: uint16(err.Code),
+ ReplyText: err.Reason,
+ },
+ &connectionCloseOk{},
+ )
+}
+
+func (c *Connection) isClosed() bool {
+ return (atomic.LoadInt32(&c.closed) == 1)
+}
+
+func (c *Connection) send(f frame) error {
+ if c.isClosed() {
+ return ErrClosed
+ }
+
+ c.sendM.Lock()
+ err := c.writer.WriteFrame(f)
+ c.sendM.Unlock()
+
+ if err != nil {
+ // shutdown could be re-entrant from signaling notify chans
+ go c.shutdown(&Error{
+ Code: FrameError,
+ Reason: err.Error(),
+ })
+ } else {
+ // Broadcast we sent a frame, reducing heartbeats, only
+ // if there is something that can receive - like a non-reentrant
+ // call or if the heartbeater isn't running
+ select {
+ case c.sends <- time.Now():
+ default:
+ }
+ }
+
+ return err
+}
+
+func (c *Connection) shutdown(err *Error) {
+ atomic.StoreInt32(&c.closed, 1)
+
+ c.destructor.Do(func() {
+ c.m.Lock()
+ closes := make([]chan *Error, len(c.closes))
+ copy(closes, c.closes)
+ c.m.Unlock()
+ if err != nil {
+ for _, c := range closes {
+ c <- err
+ }
+ }
+
+ for _, ch := range c.channels {
+ c.closeChannel(ch, err)
+ }
+
+ if err != nil {
+ c.errors <- err
+ }
+ // Shutdown handler goroutine can still receive the result.
+ close(c.errors)
+
+ c.conn.Close()
+
+ for _, c := range closes {
+ close(c)
+ }
+
+ for _, c := range c.blocks {
+ close(c)
+ }
+
+ c.m.Lock()
+ c.noNotify = true
+ c.m.Unlock()
+ })
+}
+
+// All methods sent to the connection channel should be synchronous so we
+// can handle them directly without a framing component
+func (c *Connection) demux(f frame) {
+ if f.channel() == 0 {
+ c.dispatch0(f)
+ } else {
+ c.dispatchN(f)
+ }
+}
+
+func (c *Connection) dispatch0(f frame) {
+ switch mf := f.(type) {
+ case *methodFrame:
+ switch m := mf.Method.(type) {
+ case *connectionClose:
+ // Send immediately as shutdown will close our side of the writer.
+ c.send(&methodFrame{
+ ChannelId: 0,
+ Method: &connectionCloseOk{},
+ })
+
+ c.shutdown(newError(m.ReplyCode, m.ReplyText))
+ case *connectionBlocked:
+ for _, c := range c.blocks {
+ c <- Blocking{Active: true, Reason: m.Reason}
+ }
+ case *connectionUnblocked:
+ for _, c := range c.blocks {
+ c <- Blocking{Active: false}
+ }
+ default:
+ c.rpc <- m
+ }
+ case *heartbeatFrame:
+ // kthx - all reads reset our deadline. so we can drop this
+ default:
+ // lolwat - channel0 only responds to methods and heartbeats
+ c.closeWith(ErrUnexpectedFrame)
+ }
+}
+
+func (c *Connection) dispatchN(f frame) {
+ c.m.Lock()
+ channel := c.channels[f.channel()]
+ c.m.Unlock()
+
+ if channel != nil {
+ channel.recv(channel, f)
+ } else {
+ c.dispatchClosed(f)
+ }
+}
+
+// section 2.3.7: "When a peer decides to close a channel or connection, it
+// sends a Close method. The receiving peer MUST respond to a Close with a
+// Close-Ok, and then both parties can close their channel or connection. Note
+// that if peers ignore Close, deadlock can happen when both peers send Close
+// at the same time."
+//
+// When we don't have a channel, so we must respond with close-ok on a close
+// method. This can happen between a channel exception on an asynchronous
+// method like basic.publish and a synchronous close with channel.close.
+// In that case, we'll get both a channel.close and channel.close-ok in any
+// order.
+func (c *Connection) dispatchClosed(f frame) {
+ // Only consider method frames, drop content/header frames
+ if mf, ok := f.(*methodFrame); ok {
+ switch mf.Method.(type) {
+ case *channelClose:
+ c.send(&methodFrame{
+ ChannelId: f.channel(),
+ Method: &channelCloseOk{},
+ })
+ case *channelCloseOk:
+ // we are already closed, so do nothing
+ default:
+ // unexpected method on closed channel
+ c.closeWith(ErrClosed)
+ }
+ }
+}
+
+// Reads each frame off the IO and hand off to the connection object that
+// will demux the streams and dispatch to one of the opened channels or
+// handle on channel 0 (the connection channel).
+func (c *Connection) reader(r io.Reader) {
+ buf := bufio.NewReader(r)
+ frames := &reader{buf}
+ conn, haveDeadliner := r.(readDeadliner)
+
+ for {
+ frame, err := frames.ReadFrame()
+
+ if err != nil {
+ c.shutdown(&Error{Code: FrameError, Reason: err.Error()})
+ return
+ }
+
+ c.demux(frame)
+
+ if haveDeadliner {
+ c.deadlines <- conn
+ }
+ }
+}
+
+// Ensures that at least one frame is being sent at the tuned interval with a
+// jitter tolerance of 1s
+func (c *Connection) heartbeater(interval time.Duration, done chan *Error) {
+ const maxServerHeartbeatsInFlight = 3
+
+ var sendTicks <-chan time.Time
+ if interval > 0 {
+ ticker := time.NewTicker(interval)
+ defer ticker.Stop()
+ sendTicks = ticker.C
+ }
+
+ lastSent := time.Now()
+
+ for {
+ select {
+ case at, stillSending := <-c.sends:
+ // When actively sending, depend on sent frames to reset server timer
+ if stillSending {
+ lastSent = at
+ } else {
+ return
+ }
+
+ case at := <-sendTicks:
+ // When idle, fill the space with a heartbeat frame
+ if at.Sub(lastSent) > interval-time.Second {
+ if err := c.send(&heartbeatFrame{}); err != nil {
+ // send heartbeats even after close/closeOk so we
+ // tick until the connection starts erroring
+ return
+ }
+ }
+
+ case conn := <-c.deadlines:
+ // When reading, reset our side of the deadline, if we've negotiated one with
+ // a deadline that covers at least 2 server heartbeats
+ if interval > 0 {
+ conn.SetReadDeadline(time.Now().Add(maxServerHeartbeatsInFlight * interval))
+ }
+
+ case <-done:
+ return
+ }
+ }
+}
+
+// Convenience method to inspect the Connection.Properties["capabilities"]
+// Table for server identified capabilities like "basic.ack" or
+// "confirm.select".
+func (c *Connection) isCapable(featureName string) bool {
+ capabilities, _ := c.Properties["capabilities"].(Table)
+ hasFeature, _ := capabilities[featureName].(bool)
+ return hasFeature
+}
+
+// allocateChannel records but does not open a new channel with a unique id.
+// This method is the initial part of the channel lifecycle and paired with
+// releaseChannel
+func (c *Connection) allocateChannel() (*Channel, error) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ id, ok := c.allocator.next()
+ if !ok {
+ return nil, ErrChannelMax
+ }
+
+ ch := newChannel(c, uint16(id))
+ c.channels[uint16(id)] = ch
+
+ return ch, nil
+}
+
+// releaseChannel removes a channel from the registry as the final part of the
+// channel lifecycle
+func (c *Connection) releaseChannel(id uint16) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ delete(c.channels, id)
+ c.allocator.release(int(id))
+}
+
+// openChannel allocates and opens a channel, must be paired with closeChannel
+func (c *Connection) openChannel() (*Channel, error) {
+ ch, err := c.allocateChannel()
+ if err != nil {
+ return nil, err
+ }
+
+ if err := ch.open(); err != nil {
+ return nil, err
+ }
+ return ch, nil
+}
+
+// closeChannel releases and initiates a shutdown of the channel. All channel
+// closures should be initiated here for proper channel lifecycle management on
+// this connection.
+func (c *Connection) closeChannel(ch *Channel, e *Error) {
+ ch.shutdown(e)
+ c.releaseChannel(ch.id)
+}
+
+/*
+Channel opens a unique, concurrent server channel to process the bulk of AMQP
+messages. Any error from methods on this receiver will render the receiver
+invalid and a new Channel should be opened.
+
+*/
+func (c *Connection) Channel() (*Channel, error) {
+ return c.openChannel()
+}
+
+func (c *Connection) call(req message, res ...message) error {
+ // Special case for when the protocol header frame is sent insted of a
+ // request method
+ if req != nil {
+ if err := c.send(&methodFrame{ChannelId: 0, Method: req}); err != nil {
+ return err
+ }
+ }
+
+ select {
+ case err, ok := <-c.errors:
+ if !ok {
+ return ErrClosed
+ }
+ return err
+
+ case msg := <-c.rpc:
+ // Try to match one of the result types
+ for _, try := range res {
+ if reflect.TypeOf(msg) == reflect.TypeOf(try) {
+ // *res = *msg
+ vres := reflect.ValueOf(try).Elem()
+ vmsg := reflect.ValueOf(msg).Elem()
+ vres.Set(vmsg)
+ return nil
+ }
+ }
+ return ErrCommandInvalid
+ }
+ // unreachable
+}
+
+// Connection = open-Connection *use-Connection close-Connection
+// open-Connection = C:protocol-header
+// S:START C:START-OK
+// *challenge
+// S:TUNE C:TUNE-OK
+// C:OPEN S:OPEN-OK
+// challenge = S:SECURE C:SECURE-OK
+// use-Connection = *channel
+// close-Connection = C:CLOSE S:CLOSE-OK
+// / S:CLOSE C:CLOSE-OK
+func (c *Connection) open(config Config) error {
+ if err := c.send(&protocolHeader{}); err != nil {
+ return err
+ }
+
+ return c.openStart(config)
+}
+
+func (c *Connection) openStart(config Config) error {
+ start := &connectionStart{}
+
+ if err := c.call(nil, start); err != nil {
+ return err
+ }
+
+ c.Major = int(start.VersionMajor)
+ c.Minor = int(start.VersionMinor)
+ c.Properties = Table(start.ServerProperties)
+
+ // eventually support challenge/response here by also responding to
+ // connectionSecure.
+ auth, ok := pickSASLMechanism(config.SASL, strings.Split(start.Mechanisms, " "))
+ if !ok {
+ return ErrSASL
+ }
+
+ // Save this mechanism off as the one we chose
+ c.Config.SASL = []Authentication{auth}
+
+ return c.openTune(config, auth)
+}
+
+func (c *Connection) openTune(config Config, auth Authentication) error {
+ if len(config.Properties) == 0 {
+ config.Properties = Table{
+ "product": defaultProduct,
+ "version": defaultVersion,
+ }
+ }
+
+ config.Properties["capabilities"] = Table{
+ "connection.blocked": true,
+ "consumer_cancel_notify": true,
+ }
+
+ ok := &connectionStartOk{
+ Mechanism: auth.Mechanism(),
+ Response: auth.Response(),
+ ClientProperties: config.Properties,
+ }
+ tune := &connectionTune{}
+
+ if err := c.call(ok, tune); err != nil {
+ // per spec, a connection can only be closed when it has been opened
+ // so at this point, we know it's an auth error, but the socket
+ // was closed instead. Return a meaningful error.
+ return ErrCredentials
+ }
+
+ // When the server and client both use default 0, then the max channel is
+ // only limited by uint16.
+ c.Config.ChannelMax = pick(config.ChannelMax, int(tune.ChannelMax))
+ if c.Config.ChannelMax == 0 {
+ c.Config.ChannelMax = defaultChannelMax
+ }
+ c.Config.ChannelMax = min(c.Config.ChannelMax, maxChannelMax)
+
+ // Frame size includes headers and end byte (len(payload)+8), even if
+ // this is less than FrameMinSize, use what the server sends because the
+ // alternative is to stop the handshake here.
+ c.Config.FrameSize = pick(config.FrameSize, int(tune.FrameMax))
+
+ // Save this off for resetDeadline()
+ c.Config.Heartbeat = time.Second * time.Duration(pick(
+ int(config.Heartbeat/time.Second),
+ int(tune.Heartbeat)))
+
+ // "The client should start sending heartbeats after receiving a
+ // Connection.Tune method"
+ go c.heartbeater(c.Config.Heartbeat, c.NotifyClose(make(chan *Error, 1)))
+
+ if err := c.send(&methodFrame{
+ ChannelId: 0,
+ Method: &connectionTuneOk{
+ ChannelMax: uint16(c.Config.ChannelMax),
+ FrameMax: uint32(c.Config.FrameSize),
+ Heartbeat: uint16(c.Config.Heartbeat / time.Second),
+ },
+ }); err != nil {
+ return err
+ }
+
+ return c.openVhost(config)
+}
+
+func (c *Connection) openVhost(config Config) error {
+ req := &connectionOpen{VirtualHost: config.Vhost}
+ res := &connectionOpenOk{}
+
+ if err := c.call(req, res); err != nil {
+ // Cannot be closed yet, but we know it's a vhost problem
+ return ErrVhost
+ }
+
+ c.Config.Vhost = config.Vhost
+
+ return c.openComplete()
+}
+
+// openComplete performs any final Connection initialization dependent on the
+// connection handshake and clears any state needed for TLS and AMQP handshaking.
+func (c *Connection) openComplete() error {
+ // We clear the deadlines and let the heartbeater reset the read deadline if requested.
+ // RabbitMQ uses TCP flow control at this point for pushback so Writes can
+ // intentionally block.
+ if deadliner, ok := c.conn.(interface {
+ SetDeadline(time.Time) error
+ }); ok {
+ _ = deadliner.SetDeadline(time.Time{})
+ }
+
+ c.allocator = newAllocator(1, c.Config.ChannelMax)
+ return nil
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func pick(client, server int) int {
+ if client == 0 || server == 0 {
+ return max(client, server)
+ }
+ return min(client, server)
+}
diff --git a/vendor/github.com/streadway/amqp/connection_test.go b/vendor/github.com/streadway/amqp/connection_test.go
new file mode 100644
index 00000000..ac0b6940
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/connection_test.go
@@ -0,0 +1,97 @@
+// Copyright (c) 2016, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+// +build integration
+
+package amqp
+
+import (
+ "crypto/tls"
+ "net"
+ "sync"
+ "testing"
+)
+
+func TestChannelOpenOnAClosedConnectionFails(t *testing.T) {
+ conn := integrationConnection(t, "channel on close")
+
+ conn.Close()
+
+ if _, err := conn.Channel(); err != ErrClosed {
+ t.Fatalf("channel.open on a closed connection %#v is expected to fail", conn)
+ }
+}
+
+func TestQueueDeclareOnAClosedConnectionFails(t *testing.T) {
+ conn := integrationConnection(t, "queue declare on close")
+ ch, _ := conn.Channel()
+
+ conn.Close()
+
+ if _, err := ch.QueueDeclare("an example", false, false, false, false, nil); err != ErrClosed {
+ t.Fatalf("queue.declare on a closed connection %#v is expected to return ErrClosed, returned: %#v", conn, err)
+ }
+}
+
+func TestConcurrentClose(t *testing.T) {
+ const concurrency = 32
+
+ conn := integrationConnection(t, "concurrent close")
+ defer conn.Close()
+
+ wg := sync.WaitGroup{}
+ wg.Add(concurrency)
+ for i := 0; i < concurrency; i++ {
+ go func() {
+ defer wg.Done()
+
+ err := conn.Close()
+
+ if err == nil {
+ t.Log("first concurrent close was successful")
+ return
+ }
+
+ if err == ErrClosed {
+ t.Log("later concurrent close were successful and returned ErrClosed")
+ return
+ }
+
+ // BUG(st) is this really acceptable? we got a net.OpError before the
+ // connection was marked as closed means a race condition between the
+ // network connection and handshake state. It should be a package error
+ // returned.
+ if _, neterr := err.(*net.OpError); neterr {
+ t.Logf("unknown net.OpError during close, ignoring: %+v", err)
+ return
+ }
+
+ // A different/protocol error occurred indicating a race or missed condition
+ if _, other := err.(*Error); other {
+ t.Fatalf("Expected no error, or ErrClosed, or a net.OpError from conn.Close(), got %#v (%s) of type %T", err, err, err)
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+// TestPlaintextDialTLS esnures amqp:// connections succeed when using DialTLS.
+func TestPlaintextDialTLS(t *testing.T) {
+ uri, err := ParseURI(integrationURLFromEnv())
+ if err != nil {
+ t.Fatalf("parse URI error: %s", err)
+ }
+
+ // We can only test when we have a plaintext listener
+ if uri.Scheme != "amqp" {
+ t.Skip("requires server listening for plaintext connections")
+ }
+
+ conn, err := DialTLS(uri.String(), &tls.Config{MinVersion: tls.VersionTLS12})
+ if err != nil {
+ t.Fatalf("unexpected dial error, got %v", err)
+ }
+ conn.Close()
+}
diff --git a/vendor/github.com/streadway/amqp/consumers.go b/vendor/github.com/streadway/amqp/consumers.go
new file mode 100644
index 00000000..097a15be
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/consumers.go
@@ -0,0 +1,118 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "fmt"
+ "os"
+ "sync"
+ "sync/atomic"
+)
+
+var consumerSeq uint64
+
+func uniqueConsumerTag() string {
+ return fmt.Sprintf("ctag-%s-%d", os.Args[0], atomic.AddUint64(&consumerSeq, 1))
+}
+
+type consumerBuffers map[string]chan *Delivery
+
+// Concurrent type that manages the consumerTag ->
+// ingress consumerBuffer mapping
+type consumers struct {
+ sync.Mutex
+ chans consumerBuffers
+}
+
+func makeConsumers() *consumers {
+ return &consumers{chans: make(consumerBuffers)}
+}
+
+func bufferDeliveries(in chan *Delivery, out chan Delivery) {
+ var queue []*Delivery
+ var queueIn = in
+
+ for delivery := range in {
+ select {
+ case out <- *delivery:
+ // delivered immediately while the consumer chan can receive
+ default:
+ queue = append(queue, delivery)
+ }
+
+ for len(queue) > 0 {
+ select {
+ case out <- *queue[0]:
+ queue = queue[1:]
+ case delivery, open := <-queueIn:
+ if open {
+ queue = append(queue, delivery)
+ } else {
+ // stop receiving to drain the queue
+ queueIn = nil
+ }
+ }
+ }
+ }
+
+ close(out)
+}
+
+// On key conflict, close the previous channel.
+func (subs *consumers) add(tag string, consumer chan Delivery) {
+ subs.Lock()
+ defer subs.Unlock()
+
+ if prev, found := subs.chans[tag]; found {
+ close(prev)
+ }
+
+ in := make(chan *Delivery)
+ go bufferDeliveries(in, consumer)
+
+ subs.chans[tag] = in
+}
+
+func (subs *consumers) close(tag string) (found bool) {
+ subs.Lock()
+ defer subs.Unlock()
+
+ ch, found := subs.chans[tag]
+
+ if found {
+ delete(subs.chans, tag)
+ close(ch)
+ }
+
+ return found
+}
+
+func (subs *consumers) closeAll() {
+ subs.Lock()
+ defer subs.Unlock()
+
+ for _, ch := range subs.chans {
+ close(ch)
+ }
+
+ subs.chans = make(consumerBuffers)
+}
+
+// Sends a delivery to a the consumer identified by `tag`.
+// If unbuffered channels are used for Consume this method
+// could block all deliveries until the consumer
+// receives on the other end of the channel.
+func (subs *consumers) send(tag string, msg *Delivery) bool {
+ subs.Lock()
+ defer subs.Unlock()
+
+ buffer, found := subs.chans[tag]
+ if found {
+ buffer <- msg
+ }
+
+ return found
+}
diff --git a/vendor/github.com/streadway/amqp/delivery.go b/vendor/github.com/streadway/amqp/delivery.go
new file mode 100644
index 00000000..7a4e5a6e
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/delivery.go
@@ -0,0 +1,173 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "errors"
+ "time"
+)
+
+var errDeliveryNotInitialized = errors.New("delivery not initialized")
+
+// Acknowledger notifies the server of successful or failed consumption of
+// delivieries via identifier found in the Delivery.DeliveryTag field.
+//
+// Applications can provide mock implementations in tests of Delivery handlers.
+type Acknowledger interface {
+ Ack(tag uint64, multiple bool) error
+ Nack(tag uint64, multiple bool, requeue bool) error
+ Reject(tag uint64, requeue bool) error
+}
+
+// Delivery captures the fields for a previously delivered message resident in
+// a queue to be delivered by the server to a consumer from Channel.Consume or
+// Channel.Get.
+type Delivery struct {
+ Acknowledger Acknowledger // the channel from which this delivery arrived
+
+ Headers Table // Application or header exchange table
+
+ // Properties
+ ContentType string // MIME content type
+ ContentEncoding string // MIME content encoding
+ DeliveryMode uint8 // queue implementation use - non-persistent (1) or persistent (2)
+ Priority uint8 // queue implementation use - 0 to 9
+ CorrelationId string // application use - correlation identifier
+ ReplyTo string // application use - address to to reply to (ex: RPC)
+ Expiration string // implementation use - message expiration spec
+ MessageId string // application use - message identifier
+ Timestamp time.Time // application use - message timestamp
+ Type string // application use - message type name
+ UserId string // application use - creating user - should be authenticated user
+ AppId string // application use - creating application id
+
+ // Valid only with Channel.Consume
+ ConsumerTag string
+
+ // Valid only with Channel.Get
+ MessageCount uint32
+
+ DeliveryTag uint64
+ Redelivered bool
+ Exchange string // basic.publish exhange
+ RoutingKey string // basic.publish routing key
+
+ Body []byte
+}
+
+func newDelivery(channel *Channel, msg messageWithContent) *Delivery {
+ props, body := msg.getContent()
+
+ delivery := Delivery{
+ Acknowledger: channel,
+
+ Headers: props.Headers,
+ ContentType: props.ContentType,
+ ContentEncoding: props.ContentEncoding,
+ DeliveryMode: props.DeliveryMode,
+ Priority: props.Priority,
+ CorrelationId: props.CorrelationId,
+ ReplyTo: props.ReplyTo,
+ Expiration: props.Expiration,
+ MessageId: props.MessageId,
+ Timestamp: props.Timestamp,
+ Type: props.Type,
+ UserId: props.UserId,
+ AppId: props.AppId,
+
+ Body: body,
+ }
+
+ // Properties for the delivery types
+ switch m := msg.(type) {
+ case *basicDeliver:
+ delivery.ConsumerTag = m.ConsumerTag
+ delivery.DeliveryTag = m.DeliveryTag
+ delivery.Redelivered = m.Redelivered
+ delivery.Exchange = m.Exchange
+ delivery.RoutingKey = m.RoutingKey
+
+ case *basicGetOk:
+ delivery.MessageCount = m.MessageCount
+ delivery.DeliveryTag = m.DeliveryTag
+ delivery.Redelivered = m.Redelivered
+ delivery.Exchange = m.Exchange
+ delivery.RoutingKey = m.RoutingKey
+ }
+
+ return &delivery
+}
+
+/*
+Ack delegates an acknowledgement through the Acknowledger interface that the
+client or server has finished work on a delivery.
+
+All deliveries in AMQP must be acknowledged. If you called Channel.Consume
+with autoAck true then the server will be automatically ack each message and
+this method should not be called. Otherwise, you must call Delivery.Ack after
+you have successfully processed this delivery.
+
+When multiple is true, this delivery and all prior unacknowledged deliveries
+on the same channel will be acknowledged. This is useful for batch processing
+of deliveries.
+
+An error will indicate that the acknowledge could not be delivered to the
+channel it was sent from.
+
+Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every
+delivery that is not automatically acknowledged.
+*/
+func (d Delivery) Ack(multiple bool) error {
+ if d.Acknowledger == nil {
+ return errDeliveryNotInitialized
+ }
+ return d.Acknowledger.Ack(d.DeliveryTag, multiple)
+}
+
+/*
+Reject delegates a negatively acknowledgement through the Acknowledger interface.
+
+When requeue is true, queue this message to be delivered to a consumer on a
+different channel. When requeue is false or the server is unable to queue this
+message, it will be dropped.
+
+If you are batch processing deliveries, and your server supports it, prefer
+Delivery.Nack.
+
+Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every
+delivery that is not automatically acknowledged.
+*/
+func (d Delivery) Reject(requeue bool) error {
+ if d.Acknowledger == nil {
+ return errDeliveryNotInitialized
+ }
+ return d.Acknowledger.Reject(d.DeliveryTag, requeue)
+}
+
+/*
+Nack negatively acknowledge the delivery of message(s) identified by the
+delivery tag from either the client or server.
+
+When multiple is true, nack messages up to and including delivered messages up
+until the delivery tag delivered on the same channel.
+
+When requeue is true, request the server to deliver this message to a different
+consumer. If it is not possible or requeue is false, the message will be
+dropped or delivered to a server configured dead-letter queue.
+
+This method must not be used to select or requeue messages the client wishes
+not to handle, rather it is to inform the server that the client is incapable
+of handling this message at this time.
+
+Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every
+delivery that is not automatically acknowledged.
+*/
+func (d Delivery) Nack(multiple, requeue bool) error {
+ if d.Acknowledger == nil {
+ return errDeliveryNotInitialized
+ }
+ return d.Acknowledger.Nack(d.DeliveryTag, multiple, requeue)
+}
diff --git a/vendor/github.com/streadway/amqp/delivery_test.go b/vendor/github.com/streadway/amqp/delivery_test.go
new file mode 100644
index 00000000..f126f87d
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/delivery_test.go
@@ -0,0 +1,33 @@
+package amqp
+
+import "testing"
+
+func shouldNotPanic(t *testing.T) {
+ if err := recover(); err != nil {
+ t.Fatalf("should not panic, got: %s", err)
+ }
+}
+
+// A closed delivery chan could produce zero value. Ack/Nack/Reject on these
+// deliveries can produce a nil pointer panic. Instead return an error when
+// the method can never be successful.
+func TestAckZeroValueAcknowledgerDoesNotPanic(t *testing.T) {
+ defer shouldNotPanic(t)
+ if err := (Delivery{}).Ack(false); err == nil {
+ t.Errorf("expected Delivery{}.Ack to error")
+ }
+}
+
+func TestNackZeroValueAcknowledgerDoesNotPanic(t *testing.T) {
+ defer shouldNotPanic(t)
+ if err := (Delivery{}).Nack(false, false); err == nil {
+ t.Errorf("expected Delivery{}.Ack to error")
+ }
+}
+
+func TestRejectZeroValueAcknowledgerDoesNotPanic(t *testing.T) {
+ defer shouldNotPanic(t)
+ if err := (Delivery{}).Reject(false); err == nil {
+ t.Errorf("expected Delivery{}.Ack to error")
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/doc.go b/vendor/github.com/streadway/amqp/doc.go
new file mode 100644
index 00000000..76bf3e58
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/doc.go
@@ -0,0 +1,108 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+/*
+Package amqp is an AMQP 0.9.1 client with RabbitMQ extensions
+
+Understand the AMQP 0.9.1 messaging model by reviewing these links first. Much
+of the terminology in this library directly relates to AMQP concepts.
+
+ Resources
+
+ http://www.rabbitmq.com/tutorials/amqp-concepts.html
+ http://www.rabbitmq.com/getstarted.html
+ http://www.rabbitmq.com/amqp-0-9-1-reference.html
+
+Design
+
+Most other broker clients publish to queues, but in AMQP, clients publish
+Exchanges instead. AMQP is programmable, meaning that both the producers and
+consumers agree on the configuration of the broker, instead requiring an
+operator or system configuration that declares the logical topology in the
+broker. The routing between producers and consumer queues is via Bindings.
+These bindings form the logical topology of the broker.
+
+In this library, a message sent from publisher is called a "Publishing" and a
+message received to a consumer is called a "Delivery". The fields of
+Publishings and Deliveries are close but not exact mappings to the underlying
+wire format to maintain stronger types. Many other libraries will combine
+message properties with message headers. In this library, the message well
+known properties are strongly typed fields on the Publishings and Deliveries,
+whereas the user defined headers are in the Headers field.
+
+The method naming closely matches the protocol's method name with positional
+parameters mapping to named protocol message fields. The motivation here is to
+present a comprehensive view over all possible interactions with the server.
+
+Generally, methods that map to protocol methods of the "basic" class will be
+elided in this interface, and "select" methods of various channel mode selectors
+will be elided for example Channel.Confirm and Channel.Tx.
+
+The library is intentionally designed to be synchronous, where responses for
+each protocol message are required to be received in an RPC manner. Some
+methods have a noWait parameter like Channel.QueueDeclare, and some methods are
+asynchronous like Channel.Publish. The error values should still be checked for
+these methods as they will indicate IO failures like when the underlying
+connection closes.
+
+Asynchronous Events
+
+Clients of this library may be interested in receiving some of the protocol
+messages other than Deliveries like basic.ack methods while a channel is in
+confirm mode.
+
+The Notify* methods with Connection and Channel receivers model the pattern of
+asynchronous events like closes due to exceptions, or messages that are sent out
+of band from an RPC call like basic.ack or basic.flow.
+
+Any asynchronous events, including Deliveries and Publishings must always have
+a receiver until the corresponding chans are closed. Without asynchronous
+receivers, the sychronous methods will block.
+
+Use Case
+
+It's important as a client to an AMQP topology to ensure the state of the
+broker matches your expectations. For both publish and consume use cases,
+make sure you declare the queues, exchanges and bindings you expect to exist
+prior to calling Channel.Publish or Channel.Consume.
+
+ // Connections start with amqp.Dial() typically from a command line argument
+ // or environment variable.
+ connection, err := amqp.Dial(os.Getenv("AMQP_URL"))
+
+ // To cleanly shutdown by flushing kernel buffers, make sure to close and
+ // wait for the response.
+ defer connection.Close()
+
+ // Most operations happen on a channel. If any error is returned on a
+ // channel, the channel will no longer be valid, throw it away and try with
+ // a different channel. If you use many channels, it's useful for the
+ // server to
+ channel, err := connection.Channel()
+
+ // Declare your topology here, if it doesn't exist, it will be created, if
+ // it existed already and is not what you expect, then that's considered an
+ // error.
+
+ // Use your connection on this topology with either Publish or Consume, or
+ // inspect your queues with QueueInspect. It's unwise to mix Publish and
+ // Consume to let TCP do its job well.
+
+SSL/TLS - Secure connections
+
+When Dial encounters an amqps:// scheme, it will use the zero value of a
+tls.Config. This will only perform server certificate and host verification.
+
+Use DialTLS when you wish to provide a client certificate (recommended),
+include a private certificate authority's certificate in the cert chain for
+server validity, or run insecure by not verifying the server certificate dial
+your own connection. DialTLS will use the provided tls.Config when it
+encounters an amqps:// scheme and will dial a plain connection when it
+encounters an amqp:// scheme.
+
+SSL/TLS in RabbitMQ is documented here: http://www.rabbitmq.com/ssl.html
+
+*/
+package amqp
diff --git a/vendor/github.com/streadway/amqp/examples_test.go b/vendor/github.com/streadway/amqp/examples_test.go
new file mode 100644
index 00000000..bac31fb5
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/examples_test.go
@@ -0,0 +1,402 @@
+package amqp_test
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "io/ioutil"
+ "log"
+ "net"
+ "runtime"
+ "time"
+
+ "github.com/streadway/amqp"
+)
+
+func ExampleConfig_timeout() {
+ // Provide your own anonymous Dial function that delgates to net.DialTimout
+ // for custom timeouts
+
+ conn, err := amqp.DialConfig("amqp:///", amqp.Config{
+ Dial: func(network, addr string) (net.Conn, error) {
+ return net.DialTimeout(network, addr, 2*time.Second)
+ },
+ })
+
+ log.Printf("conn: %v, err: %v", conn, err)
+}
+
+func ExampleDialTLS() {
+ // This example assume you have a RabbitMQ node running on localhost
+ // with TLS enabled.
+ //
+ // The easiest way to create the CA, certificates and keys required for these
+ // examples is by using tls-gen: https://github.com/michaelklishin/tls-gen
+ //
+ // A comprehensive RabbitMQ TLS guide can be found at
+ // http://www.rabbitmq.com/ssl.html
+ //
+ // Once you have the required TLS files in place, use the following
+ // rabbitmq.config example for the RabbitMQ node that you will run on
+ // localhost:
+ //
+ // [
+ // {rabbit, [
+ // {tcp_listeners, []}, % listens on 127.0.0.1:5672
+ // {ssl_listeners, [5671]}, % listens on 0.0.0.0:5671
+ // {ssl_options, [{cacertfile,"/path/to/your/testca/cacert.pem"},
+ // {certfile,"/path/to/your/server/cert.pem"},
+ // {keyfile,"/path/to/your/server/key.pem"},
+ // {verify,verify_peer},
+ // {fail_if_no_peer_cert,true}]}
+ // ]}
+ // ].
+ //
+ //
+ // In the above rabbitmq.config example, we are disabling the plain AMQP port
+ // and verifying that clients and fail if no certificate is presented.
+ //
+ // The self-signing certificate authority's certificate (cacert.pem) must be
+ // included in the RootCAs to be trusted, otherwise the server certificate
+ // will fail certificate verification.
+ //
+ // Alternatively to adding it to the tls.Config. you can add the CA's cert to
+ // your system's root CAs. The tls package will use the system roots
+ // specific to each support OS. Under OS X, add (drag/drop) cacert.pem
+ // file to the 'Certificates' section of KeyChain.app to add and always
+ // trust. You can also add it via the command line:
+ //
+ // security add-certificate testca/cacert.pem
+ // security add-trusted-cert testca/cacert.pem
+ //
+ // If you depend on the system root CAs, then use nil for the RootCAs field
+ // so the system roots will be loaded instead.
+ //
+ // Server names are validated by the crypto/tls package, so the server
+ // certificate must be made for the hostname in the URL. Find the commonName
+ // (CN) and make sure the hostname in the URL matches this common name. Per
+ // the RabbitMQ instructions (or tls-gen) for a self-signed cert, this defaults to the
+ // current hostname.
+ //
+ // openssl x509 -noout -in /path/to/certificate.pem -subject
+ //
+ // If your server name in your certificate is different than the host you are
+ // connecting to, set the hostname used for verification in
+ // ServerName field of the tls.Config struct.
+ cfg := new(tls.Config)
+
+ // see at the top
+ cfg.RootCAs = x509.NewCertPool()
+
+ if ca, err := ioutil.ReadFile("testca/cacert.pem"); err == nil {
+ cfg.RootCAs.AppendCertsFromPEM(ca)
+ }
+
+ // Move the client cert and key to a location specific to your application
+ // and load them here.
+
+ if cert, err := tls.LoadX509KeyPair("client/cert.pem", "client/key.pem"); err == nil {
+ cfg.Certificates = append(cfg.Certificates, cert)
+ }
+
+ // see a note about Common Name (CN) at the top
+ conn, err := amqp.DialTLS("amqps://server-name-from-certificate/", cfg)
+
+ log.Printf("conn: %v, err: %v", conn, err)
+}
+
+func ExampleChannel_Confirm_bridge() {
+ // This example acts as a bridge, shoveling all messages sent from the source
+ // exchange "log" to destination exchange "log".
+
+ // Confirming publishes can help from overproduction and ensure every message
+ // is delivered.
+
+ // Setup the source of the store and forward
+ source, err := amqp.Dial("amqp://source/")
+ if err != nil {
+ log.Fatalf("connection.open source: %s", err)
+ }
+ defer source.Close()
+
+ chs, err := source.Channel()
+ if err != nil {
+ log.Fatalf("channel.open source: %s", err)
+ }
+
+ if err := chs.ExchangeDeclare("log", "topic", true, false, false, false, nil); err != nil {
+ log.Fatalf("exchange.declare destination: %s", err)
+ }
+
+ if _, err := chs.QueueDeclare("remote-tee", true, true, false, false, nil); err != nil {
+ log.Fatalf("queue.declare source: %s", err)
+ }
+
+ if err := chs.QueueBind("remote-tee", "#", "logs", false, nil); err != nil {
+ log.Fatalf("queue.bind source: %s", err)
+ }
+
+ shovel, err := chs.Consume("remote-tee", "shovel", false, false, false, false, nil)
+ if err != nil {
+ log.Fatalf("basic.consume source: %s", err)
+ }
+
+ // Setup the destination of the store and forward
+ destination, err := amqp.Dial("amqp://destination/")
+ if err != nil {
+ log.Fatalf("connection.open destination: %s", err)
+ }
+ defer destination.Close()
+
+ chd, err := destination.Channel()
+ if err != nil {
+ log.Fatalf("channel.open destination: %s", err)
+ }
+
+ if err := chd.ExchangeDeclare("log", "topic", true, false, false, false, nil); err != nil {
+ log.Fatalf("exchange.declare destination: %s", err)
+ }
+
+ // Buffer of 1 for our single outstanding publishing
+ confirms := chd.NotifyPublish(make(chan amqp.Confirmation, 1))
+
+ if err := chd.Confirm(false); err != nil {
+ log.Fatalf("confirm.select destination: %s", err)
+ }
+
+ // Now pump the messages, one by one, a smarter implementation
+ // would batch the deliveries and use multiple ack/nacks
+ for {
+ msg, ok := <-shovel
+ if !ok {
+ log.Fatalf("source channel closed, see the reconnect example for handling this")
+ }
+
+ err = chd.Publish("logs", msg.RoutingKey, false, false, amqp.Publishing{
+ // Copy all the properties
+ ContentType: msg.ContentType,
+ ContentEncoding: msg.ContentEncoding,
+ DeliveryMode: msg.DeliveryMode,
+ Priority: msg.Priority,
+ CorrelationId: msg.CorrelationId,
+ ReplyTo: msg.ReplyTo,
+ Expiration: msg.Expiration,
+ MessageId: msg.MessageId,
+ Timestamp: msg.Timestamp,
+ Type: msg.Type,
+ UserId: msg.UserId,
+ AppId: msg.AppId,
+
+ // Custom headers
+ Headers: msg.Headers,
+
+ // And the body
+ Body: msg.Body,
+ })
+
+ if err != nil {
+ msg.Nack(false, false)
+ log.Fatalf("basic.publish destination: %+v", msg)
+ }
+
+ // only ack the source delivery when the destination acks the publishing
+ if confirmed := <-confirms; confirmed.Ack {
+ msg.Ack(false)
+ } else {
+ msg.Nack(false, false)
+ }
+ }
+}
+
+func ExampleChannel_Consume() {
+ // Connects opens an AMQP connection from the credentials in the URL.
+ conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
+ if err != nil {
+ log.Fatalf("connection.open: %s", err)
+ }
+ defer conn.Close()
+
+ c, err := conn.Channel()
+ if err != nil {
+ log.Fatalf("channel.open: %s", err)
+ }
+
+ // We declare our topology on both the publisher and consumer to ensure they
+ // are the same. This is part of AMQP being a programmable messaging model.
+ //
+ // See the Channel.Publish example for the complimentary declare.
+ err = c.ExchangeDeclare("logs", "topic", true, false, false, false, nil)
+ if err != nil {
+ log.Fatalf("exchange.declare: %s", err)
+ }
+
+ // Establish our queue topologies that we are responsible for
+ type bind struct {
+ queue string
+ key string
+ }
+
+ bindings := []bind{
+ bind{"page", "alert"},
+ bind{"email", "info"},
+ bind{"firehose", "#"},
+ }
+
+ for _, b := range bindings {
+ _, err = c.QueueDeclare(b.queue, true, false, false, false, nil)
+ if err != nil {
+ log.Fatalf("queue.declare: %v", err)
+ }
+
+ err = c.QueueBind(b.queue, b.key, "logs", false, nil)
+ if err != nil {
+ log.Fatalf("queue.bind: %v", err)
+ }
+ }
+
+ // Set our quality of service. Since we're sharing 3 consumers on the same
+ // channel, we want at least 3 messages in flight.
+ err = c.Qos(3, 0, false)
+ if err != nil {
+ log.Fatalf("basic.qos: %v", err)
+ }
+
+ // Establish our consumers that have different responsibilities. Our first
+ // two queues do not ack the messages on the server, so require to be acked
+ // on the client.
+
+ pages, err := c.Consume("page", "pager", false, false, false, false, nil)
+ if err != nil {
+ log.Fatalf("basic.consume: %v", err)
+ }
+
+ go func() {
+ for log := range pages {
+ // ... this consumer is responsible for sending pages per log
+ log.Ack(false)
+ }
+ }()
+
+ // Notice how the concern for which messages arrive here are in the AMQP
+ // topology and not in the queue. We let the server pick a consumer tag this
+ // time.
+
+ emails, err := c.Consume("email", "", false, false, false, false, nil)
+ if err != nil {
+ log.Fatalf("basic.consume: %v", err)
+ }
+
+ go func() {
+ for log := range emails {
+ // ... this consumer is responsible for sending emails per log
+ log.Ack(false)
+ }
+ }()
+
+ // This consumer requests that every message is acknowledged as soon as it's
+ // delivered.
+
+ firehose, err := c.Consume("firehose", "", true, false, false, false, nil)
+ if err != nil {
+ log.Fatalf("basic.consume: %v", err)
+ }
+
+ // To show how to process the items in parallel, we'll use a work pool.
+ for i := 0; i < runtime.NumCPU(); i++ {
+ go func(work <-chan amqp.Delivery) {
+ for _ = range work {
+ // ... this consumer pulls from the firehose and doesn't need to acknowledge
+ }
+ }(firehose)
+ }
+
+ // Wait until you're ready to finish, could be a signal handler here.
+ time.Sleep(10 * time.Second)
+
+ // Cancelling a consumer by name will finish the range and gracefully end the
+ // goroutine
+ err = c.Cancel("pager", false)
+ if err != nil {
+ log.Fatalf("basic.cancel: %v", err)
+ }
+
+ // deferred closing the Connection will also finish the consumer's ranges of
+ // their delivery chans. If you need every delivery to be processed, make
+ // sure to wait for all consumers goroutines to finish before exiting your
+ // process.
+}
+
+func ExampleChannel_Publish() {
+ // Connects opens an AMQP connection from the credentials in the URL.
+ conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
+ if err != nil {
+ log.Fatalf("connection.open: %s", err)
+ }
+
+ // This waits for a server acknowledgment which means the sockets will have
+ // flushed all outbound publishings prior to returning. It's important to
+ // block on Close to not lose any publishings.
+ defer conn.Close()
+
+ c, err := conn.Channel()
+ if err != nil {
+ log.Fatalf("channel.open: %s", err)
+ }
+
+ // We declare our topology on both the publisher and consumer to ensure they
+ // are the same. This is part of AMQP being a programmable messaging model.
+ //
+ // See the Channel.Consume example for the complimentary declare.
+ err = c.ExchangeDeclare("logs", "topic", true, false, false, false, nil)
+ if err != nil {
+ log.Fatalf("exchange.declare: %v", err)
+ }
+
+ // Prepare this message to be persistent. Your publishing requirements may
+ // be different.
+ msg := amqp.Publishing{
+ DeliveryMode: amqp.Persistent,
+ Timestamp: time.Now(),
+ ContentType: "text/plain",
+ Body: []byte("Go Go AMQP!"),
+ }
+
+ // This is not a mandatory delivery, so it will be dropped if there are no
+ // queues bound to the logs exchange.
+ err = c.Publish("logs", "info", false, false, msg)
+ if err != nil {
+ // Since publish is asynchronous this can happen if the network connection
+ // is reset or if the server has run out of resources.
+ log.Fatalf("basic.publish: %v", err)
+ }
+}
+
+func publishAllTheThings(conn *amqp.Connection) {
+ // ... snarf snarf, barf barf
+}
+
+func ExampleConnection_NotifyBlocked() {
+ // Simply logs when the server throttles the TCP connection for publishers
+
+ // Test this by tuning your server to have a low memory watermark:
+ // rabbitmqctl set_vm_memory_high_watermark 0.00000001
+
+ conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
+ if err != nil {
+ log.Fatalf("connection.open: %s", err)
+ }
+ defer conn.Close()
+
+ blockings := conn.NotifyBlocked(make(chan amqp.Blocking))
+ go func() {
+ for b := range blockings {
+ if b.Active {
+ log.Printf("TCP blocked: %q", b.Reason)
+ } else {
+ log.Printf("TCP unblocked")
+ }
+ }
+ }()
+
+ // Your application domain channel setup publishings
+ publishAllTheThings(conn)
+}
diff --git a/vendor/github.com/streadway/amqp/fuzz.go b/vendor/github.com/streadway/amqp/fuzz.go
new file mode 100644
index 00000000..16e626ce
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/fuzz.go
@@ -0,0 +1,17 @@
+// +build gofuzz
+
+package amqp
+
+import "bytes"
+
+func Fuzz(data []byte) int {
+ r := reader{bytes.NewReader(data)}
+ frame, err := r.ReadFrame()
+ if err != nil {
+ if frame != nil {
+ panic("frame is not nil")
+ }
+ return 0
+ }
+ return 1
+}
diff --git a/vendor/github.com/streadway/amqp/gen.sh b/vendor/github.com/streadway/amqp/gen.sh
new file mode 100755
index 00000000..d46e19bd
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/gen.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+go run spec/gen.go < spec/amqp0-9-1.stripped.extended.xml | gofmt > spec091.go
diff --git a/vendor/github.com/streadway/amqp/integration_test.go b/vendor/github.com/streadway/amqp/integration_test.go
new file mode 100644
index 00000000..13eb2d29
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/integration_test.go
@@ -0,0 +1,1834 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+// +build integration
+
+package amqp
+
+import (
+ "bytes"
+ devrand "crypto/rand"
+ "encoding/binary"
+ "fmt"
+ "hash/crc32"
+ "io"
+ "math/rand"
+ "net"
+ "os"
+ "reflect"
+ "strconv"
+ "sync"
+ "testing"
+ "testing/quick"
+ "time"
+)
+
+func TestIntegrationOpenClose(t *testing.T) {
+ if c := integrationConnection(t, "open-close"); c != nil {
+ t.Logf("have connection, calling connection close")
+ if err := c.Close(); err != nil {
+ t.Fatalf("connection close: %s", err)
+ }
+ t.Logf("connection close OK")
+ }
+}
+
+func TestIntegrationOpenCloseChannel(t *testing.T) {
+ if c := integrationConnection(t, "channel"); c != nil {
+ defer c.Close()
+
+ if _, err := c.Channel(); err != nil {
+ t.Errorf("Channel could not be opened: %s", err)
+ }
+ }
+}
+
+func TestIntegrationOpenConfig(t *testing.T) {
+ config := Config{}
+
+ c, err := DialConfig(integrationURLFromEnv(), config)
+ if err != nil {
+ t.Fatalf("expected to dial with config %+v integration server: %s", config, err)
+ }
+
+ if _, err := c.Channel(); err != nil {
+ t.Fatalf("expected to open channel: %s", err)
+ }
+
+ if err := c.Close(); err != nil {
+ t.Fatalf("connection close: %s", err)
+ }
+}
+
+func TestIntegrationOpenConfigWithNetDial(t *testing.T) {
+ config := Config{Dial: net.Dial}
+
+ c, err := DialConfig(integrationURLFromEnv(), config)
+ if err != nil {
+ t.Errorf("expected to dial with config %+v integration server: %s", config, err)
+ }
+
+ if _, err := c.Channel(); err != nil {
+ t.Fatalf("expected to open channel: %s", err)
+ }
+
+ if err := c.Close(); err != nil {
+ t.Fatalf("connection close: %s", err)
+ }
+}
+
+func TestIntegrationLocalAddr(t *testing.T) {
+ config := Config{}
+
+ c, err := DialConfig(integrationURLFromEnv(), config)
+ defer c.Close()
+ if err != nil {
+ t.Errorf("expected to dial with config %+v integration server: %s", config, err)
+ }
+
+ a := c.LocalAddr()
+ _, portString, err := net.SplitHostPort(a.String())
+ if err != nil {
+ t.Errorf("expected to get a local network address with config %+v integration server: %s", config, a.String())
+ }
+
+ port, err := strconv.Atoi(portString)
+ if err != nil {
+ t.Errorf("expected to get a TCP port number with config %+v integration server: %s", config, err)
+ }
+ t.Logf("Connected to port %d\n", port)
+}
+
+// https://github.com/streadway/amqp/issues/94
+func TestExchangePassiveOnMissingExchangeShouldError(t *testing.T) {
+ c := integrationConnection(t, "exch")
+ if c != nil {
+ defer c.Close()
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("create channel 1: %s", err)
+ }
+ defer ch.Close()
+
+ if err := ch.ExchangeDeclarePassive(
+ "test-integration-missing-passive-exchange",
+ "direct", // type
+ false, // duration (note: is durable)
+ true, // auto-delete
+ false, // internal
+ false, // nowait
+ nil, // args
+ ); err == nil {
+ t.Fatal("ExchangeDeclarePassive of a missing exchange should return error")
+ }
+ }
+}
+
+// https://github.com/streadway/amqp/issues/94
+func TestIntegrationExchangeDeclarePassiveOnDeclaredShouldNotError(t *testing.T) {
+ c := integrationConnection(t, "exch")
+ if c != nil {
+ defer c.Close()
+
+ exchange := "test-integration-decalred-passive-exchange"
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("create channel: %s", err)
+ }
+ defer ch.Close()
+
+ if err := ch.ExchangeDeclare(
+ exchange, // name
+ "direct", // type
+ false, // durable
+ true, // auto-delete
+ false, // internal
+ false, // nowait
+ nil, // args
+ ); err != nil {
+ t.Fatalf("declare exchange: %s", err)
+ }
+
+ if err := ch.ExchangeDeclarePassive(
+ exchange, // name
+ "direct", // type
+ false, // durable
+ true, // auto-delete
+ false, // internal
+ false, // nowait
+ nil, // args
+ ); err != nil {
+ t.Fatalf("ExchangeDeclarePassive on a declared exchange should not error, got: %q", err)
+ }
+ }
+}
+
+func TestIntegrationExchange(t *testing.T) {
+ c := integrationConnection(t, "exch")
+ if c != nil {
+ defer c.Close()
+
+ channel, err := c.Channel()
+ if err != nil {
+ t.Fatalf("create channel: %s", err)
+ }
+ t.Logf("create channel OK")
+
+ exchange := "test-integration-exchange"
+
+ if err := channel.ExchangeDeclare(
+ exchange, // name
+ "direct", // type
+ false, // duration
+ true, // auto-delete
+ false, // internal
+ false, // nowait
+ nil, // args
+ ); err != nil {
+ t.Fatalf("declare exchange: %s", err)
+ }
+ t.Logf("declare exchange OK")
+
+ if err := channel.ExchangeDelete(exchange, false, false); err != nil {
+ t.Fatalf("delete exchange: %s", err)
+ }
+ t.Logf("delete exchange OK")
+
+ if err := channel.Close(); err != nil {
+ t.Fatalf("close channel: %s", err)
+ }
+ t.Logf("close channel OK")
+ }
+}
+
+// https://github.com/streadway/amqp/issues/94
+func TestIntegrationQueueDeclarePassiveOnMissingExchangeShouldError(t *testing.T) {
+ c := integrationConnection(t, "queue")
+ if c != nil {
+ defer c.Close()
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("create channel1: %s", err)
+ }
+ defer ch.Close()
+
+ if _, err := ch.QueueDeclarePassive(
+ "test-integration-missing-passive-queue", // name
+ false, // duration (note: not durable)
+ true, // auto-delete
+ false, // exclusive
+ false, // noWait
+ nil, // arguments
+ ); err == nil {
+ t.Fatal("QueueDeclarePassive of a missing queue should error")
+ }
+ }
+}
+
+// https://github.com/streadway/amqp/issues/94
+func TestIntegrationPassiveQueue(t *testing.T) {
+ c := integrationConnection(t, "queue")
+ if c != nil {
+ defer c.Close()
+
+ name := "test-integration-declared-passive-queue"
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("create channel1: %s", err)
+ }
+ defer ch.Close()
+
+ if _, err := ch.QueueDeclare(
+ name, // name
+ false, // durable
+ true, // auto-delete
+ false, // exclusive
+ false, // noWait
+ nil, // arguments
+ ); err != nil {
+ t.Fatalf("queue declare: %s", err)
+ }
+
+ if _, err := ch.QueueDeclarePassive(
+ name, // name
+ false, // durable
+ true, // auto-delete
+ false, // exclusive
+ false, // noWait
+ nil, // arguments
+ ); err != nil {
+ t.Fatalf("QueueDeclarePassive on declared queue should not error, got: %q", err)
+ }
+
+ if _, err := ch.QueueDeclarePassive(
+ name, // name
+ true, // durable (note: differs)
+ true, // auto-delete
+ false, // exclusive
+ false, // noWait
+ nil, // arguments
+ ); err != nil {
+ t.Fatalf("QueueDeclarePassive on declared queue with different flags should error")
+ }
+ }
+}
+
+func TestIntegrationBasicQueueOperations(t *testing.T) {
+ c := integrationConnection(t, "queue")
+ if c != nil {
+ defer c.Close()
+
+ channel, err := c.Channel()
+ if err != nil {
+ t.Fatalf("create channel: %s", err)
+ }
+ t.Logf("create channel OK")
+
+ exchangeName := "test-basic-ops-exchange"
+ queueName := "test-basic-ops-queue"
+
+ deleteQueueFirstOptions := []bool{true, false}
+ for _, deleteQueueFirst := range deleteQueueFirstOptions {
+
+ if err := channel.ExchangeDeclare(
+ exchangeName, // name
+ "direct", // type
+ true, // duration (note: is durable)
+ false, // auto-delete
+ false, // internal
+ false, // nowait
+ nil, // args
+ ); err != nil {
+ t.Fatalf("declare exchange: %s", err)
+ }
+ t.Logf("declare exchange OK")
+
+ if _, err := channel.QueueDeclare(
+ queueName, // name
+ true, // duration (note: durable)
+ false, // auto-delete
+ false, // exclusive
+ false, // noWait
+ nil, // arguments
+ ); err != nil {
+ t.Fatalf("queue declare: %s", err)
+ }
+ t.Logf("declare queue OK")
+
+ if err := channel.QueueBind(
+ queueName, // name
+ "", // routingKey
+ exchangeName, // sourceExchange
+ false, // noWait
+ nil, // arguments
+ ); err != nil {
+ t.Fatalf("queue bind: %s", err)
+ }
+ t.Logf("queue bind OK")
+
+ if deleteQueueFirst {
+ if _, err := channel.QueueDelete(
+ queueName, // name
+ false, // ifUnused (false=be aggressive)
+ false, // ifEmpty (false=be aggressive)
+ false, // noWait
+ ); err != nil {
+ t.Fatalf("delete queue (first): %s", err)
+ }
+ t.Logf("delete queue (first) OK")
+
+ if err := channel.ExchangeDelete(exchangeName, false, false); err != nil {
+ t.Fatalf("delete exchange (after delete queue): %s", err)
+ }
+ t.Logf("delete exchange (after delete queue) OK")
+
+ } else { // deleteExchangeFirst
+ if err := channel.ExchangeDelete(exchangeName, false, false); err != nil {
+ t.Fatalf("delete exchange (first): %s", err)
+ }
+ t.Logf("delete exchange (first) OK")
+
+ if _, err := channel.QueueInspect(queueName); err != nil {
+ t.Fatalf("inspect queue state after deleting exchange: %s", err)
+ }
+ t.Logf("queue properly remains after exchange is deleted")
+
+ if _, err := channel.QueueDelete(
+ queueName,
+ false, // ifUnused
+ false, // ifEmpty
+ false, // noWait
+ ); err != nil {
+ t.Fatalf("delete queue (after delete exchange): %s", err)
+ }
+ t.Logf("delete queue (after delete exchange) OK")
+ }
+ }
+
+ if err := channel.Close(); err != nil {
+ t.Fatalf("close channel: %s", err)
+ }
+ t.Logf("close channel OK")
+ }
+}
+
+func TestIntegrationConnectionNegotiatesMaxChannels(t *testing.T) {
+ config := Config{ChannelMax: 0}
+
+ c, err := DialConfig(integrationURLFromEnv(), config)
+ if err != nil {
+ t.Errorf("expected to dial with config %+v integration server: %s", config, err)
+ }
+ defer c.Close()
+
+ if want, got := defaultChannelMax, c.Config.ChannelMax; want != got {
+ t.Fatalf("expected connection to negotiate uint16 (%d) channels, got: %d", want, got)
+ }
+}
+
+func TestIntegrationConnectionNegotiatesClientMaxChannels(t *testing.T) {
+ config := Config{ChannelMax: 16}
+
+ c, err := DialConfig(integrationURLFromEnv(), config)
+ if err != nil {
+ t.Errorf("expected to dial with config %+v integration server: %s", config, err)
+ }
+ defer c.Close()
+
+ if want, got := config.ChannelMax, c.Config.ChannelMax; want != got {
+ t.Fatalf("expected client specified channel limit after handshake %d, got: %d", want, got)
+ }
+}
+
+func TestIntegrationChannelIDsExhausted(t *testing.T) {
+ config := Config{ChannelMax: 16}
+
+ c, err := DialConfig(integrationURLFromEnv(), config)
+ if err != nil {
+ t.Errorf("expected to dial with config %+v integration server: %s", config, err)
+ }
+ defer c.Close()
+
+ for i := 1; i <= c.Config.ChannelMax; i++ {
+ if _, err := c.Channel(); err != nil {
+ t.Fatalf("expected allocating all channel ids to succed, failed on %d with %v", i, err)
+ }
+ }
+
+ if _, err := c.Channel(); err != ErrChannelMax {
+ t.Fatalf("expected allocating all channels to produce the client side error %#v, got: %#v", ErrChannelMax, err)
+ }
+}
+
+func TestIntegrationChannelClosing(t *testing.T) {
+ c := integrationConnection(t, "closings")
+ if c != nil {
+ defer c.Close()
+
+ // This function is run on every channel after it is successfully
+ // opened. It can do something to verify something. It should be
+ // quick; many channels may be opened!
+ f := func(t *testing.T, c *Channel) {
+ return
+ }
+
+ // open and close
+ channel, err := c.Channel()
+ if err != nil {
+ t.Fatalf("basic create channel: %s", err)
+ }
+ t.Logf("basic create channel OK")
+
+ if err := channel.Close(); err != nil {
+ t.Fatalf("basic close channel: %s", err)
+ }
+ t.Logf("basic close channel OK")
+
+ // deferred close
+ signal := make(chan bool)
+ go func() {
+ channel, err := c.Channel()
+ if err != nil {
+ t.Fatalf("second create channel: %s", err)
+ }
+ t.Logf("second create channel OK")
+
+ <-signal // a bit of synchronization
+ f(t, channel)
+
+ defer func() {
+ if err := channel.Close(); err != nil {
+ t.Fatalf("deferred close channel: %s", err)
+ }
+ t.Logf("deferred close channel OK")
+ signal <- true
+ }()
+ }()
+ signal <- true
+ select {
+ case <-signal:
+ t.Logf("(got close signal OK)")
+ break
+ case <-time.After(250 * time.Millisecond):
+ t.Fatalf("deferred close: timeout")
+ }
+
+ // multiple channels
+ for _, n := range []int{2, 4, 8, 16, 32, 64, 128, 256} {
+ channels := make([]*Channel, n)
+ for i := 0; i < n; i++ {
+ var err error
+ if channels[i], err = c.Channel(); err != nil {
+ t.Fatalf("create channel %d/%d: %s", i+1, n, err)
+ }
+ }
+ f(t, channel)
+ for i, channel := range channels {
+ if err := channel.Close(); err != nil {
+ t.Fatalf("close channel %d/%d: %s", i+1, n, err)
+ }
+ }
+ t.Logf("created/closed %d channels OK", n)
+ }
+
+ }
+}
+
+func TestIntegrationMeaningfulChannelErrors(t *testing.T) {
+ c := integrationConnection(t, "pub")
+ if c != nil {
+ defer c.Close()
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("Could not create channel")
+ }
+
+ queue := "test.integration.channel.error"
+
+ _, err = ch.QueueDeclare(queue, false, true, false, false, nil)
+ if err != nil {
+ t.Fatalf("Could not declare")
+ }
+
+ _, err = ch.QueueDeclare(queue, true, false, false, false, nil)
+ if err == nil {
+ t.Fatalf("Expected error, got nil")
+ }
+
+ e, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("Expected type Error response, got %T", err)
+ }
+
+ if e.Code != PreconditionFailed {
+ t.Fatalf("Expected PreconditionFailed, got: %+v", e)
+ }
+
+ _, err = ch.QueueDeclare(queue, false, true, false, false, nil)
+ if err != ErrClosed {
+ t.Fatalf("Expected channel to be closed, got: %T", err)
+ }
+ }
+}
+
+// https://github.com/streadway/amqp/issues/6
+func TestIntegrationNonBlockingClose(t *testing.T) {
+ c := integrationConnection(t, "#6")
+ if c != nil {
+ defer c.Close()
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("Could not create channel")
+ }
+
+ queue := "test.integration.blocking.close"
+
+ _, err = ch.QueueDeclare(queue, false, true, false, false, nil)
+ if err != nil {
+ t.Fatalf("Could not declare")
+ }
+
+ msgs, err := ch.Consume(queue, "", false, false, false, false, nil)
+ if err != nil {
+ t.Fatalf("Could not consume")
+ }
+
+ // Simulate a consumer
+ go func() {
+ for _ = range msgs {
+ t.Logf("Oh my, received message on an empty queue")
+ }
+ }()
+
+ succeed := make(chan bool)
+
+ go func() {
+ if err = ch.Close(); err != nil {
+ t.Fatalf("Close produced an error when it shouldn't")
+ }
+ succeed <- true
+ }()
+
+ select {
+ case <-succeed:
+ break
+ case <-time.After(1 * time.Second):
+ t.Fatalf("Close timed out after 1s")
+ }
+ }
+}
+
+func TestIntegrationPublishConsume(t *testing.T) {
+ queue := "test.integration.publish.consume"
+
+ c1 := integrationConnection(t, "pub")
+ c2 := integrationConnection(t, "sub")
+
+ if c1 != nil && c2 != nil {
+ defer c1.Close()
+ defer c2.Close()
+
+ pub, _ := c1.Channel()
+ sub, _ := c2.Channel()
+
+ pub.QueueDeclare(queue, false, true, false, false, nil)
+ sub.QueueDeclare(queue, false, true, false, false, nil)
+ defer pub.QueueDelete(queue, false, false, false)
+
+ messages, _ := sub.Consume(queue, "", false, false, false, false, nil)
+
+ pub.Publish("", queue, false, false, Publishing{Body: []byte("pub 1")})
+ pub.Publish("", queue, false, false, Publishing{Body: []byte("pub 2")})
+ pub.Publish("", queue, false, false, Publishing{Body: []byte("pub 3")})
+
+ assertConsumeBody(t, messages, []byte("pub 1"))
+ assertConsumeBody(t, messages, []byte("pub 2"))
+ assertConsumeBody(t, messages, []byte("pub 3"))
+ }
+}
+
+func TestIntegrationConsumeFlow(t *testing.T) {
+ queue := "test.integration.consumer-flow"
+
+ c1 := integrationConnection(t, "pub-flow")
+ c2 := integrationConnection(t, "sub-flow")
+
+ if c1 != nil && c2 != nil {
+ defer c1.Close()
+ defer c2.Close()
+
+ pub, _ := c1.Channel()
+ sub, _ := c2.Channel()
+
+ pub.QueueDeclare(queue, false, true, false, false, nil)
+ sub.QueueDeclare(queue, false, true, false, false, nil)
+ defer pub.QueueDelete(queue, false, false, false)
+
+ sub.Qos(1, 0, false)
+
+ messages, _ := sub.Consume(queue, "", false, false, false, false, nil)
+
+ pub.Publish("", queue, false, false, Publishing{Body: []byte("pub 1")})
+ pub.Publish("", queue, false, false, Publishing{Body: []byte("pub 2")})
+
+ msg := assertConsumeBody(t, messages, []byte("pub 1"))
+
+ if err := sub.Flow(false); err.(*Error).Code == NotImplemented {
+ t.Log("flow control is not supported on this version of rabbitmq")
+ return
+ }
+
+ msg.Ack(false)
+
+ select {
+ case <-messages:
+ t.Fatalf("message was delivered when flow was not active")
+ default:
+ }
+
+ sub.Flow(true)
+
+ msg = assertConsumeBody(t, messages, []byte("pub 2"))
+ msg.Ack(false)
+ }
+}
+
+func TestIntegrationRecoverNotImplemented(t *testing.T) {
+ queue := "test.recover"
+
+ if c, ch := integrationQueue(t, queue); c != nil {
+ if product, ok := c.Properties["product"]; ok && product.(string) == "RabbitMQ" {
+ defer c.Close()
+
+ err := ch.Recover(false)
+
+ if ex, ok := err.(*Error); !ok || ex.Code != 540 {
+ t.Fatalf("Expected NOT IMPLEMENTED got: %v", ex)
+ }
+ }
+ }
+}
+
+// This test is driven by a private API to simulate the server sending a channelFlow message
+func TestIntegrationPublishFlow(t *testing.T) {
+ // TODO - no idea how to test without affecting the server or mucking internal APIs
+ // i'd like to make sure the RW lock can be held by multiple publisher threads
+ // and that multiple channelFlow messages do not block the dispatch thread
+}
+
+func TestIntegrationConsumeCancel(t *testing.T) {
+ queue := "test.integration.consume-cancel"
+
+ c := integrationConnection(t, "pub")
+
+ if c != nil {
+ defer c.Close()
+
+ ch, _ := c.Channel()
+
+ ch.QueueDeclare(queue, false, true, false, false, nil)
+ defer ch.QueueDelete(queue, false, false, false)
+
+ messages, _ := ch.Consume(queue, "integration-tag", false, false, false, false, nil)
+
+ ch.Publish("", queue, false, false, Publishing{Body: []byte("1")})
+
+ assertConsumeBody(t, messages, []byte("1"))
+
+ err := ch.Cancel("integration-tag", false)
+ if err != nil {
+ t.Fatalf("error cancelling the consumer: %v", err)
+ }
+
+ ch.Publish("", queue, false, false, Publishing{Body: []byte("2")})
+
+ select {
+ case <-time.After(100 * time.Millisecond):
+ t.Fatalf("Timeout on Close")
+ case _, ok := <-messages:
+ if ok {
+ t.Fatalf("Extra message on consumer when consumer should have been closed")
+ }
+ }
+ }
+}
+
+func (c *Connection) Generate(r *rand.Rand, _ int) reflect.Value {
+ urlStr := os.Getenv("AMQP_URL")
+ if urlStr == "" {
+ return reflect.ValueOf(nil)
+ }
+
+ conn, err := Dial(urlStr)
+ if err != nil {
+ return reflect.ValueOf(nil)
+ }
+
+ return reflect.ValueOf(conn)
+}
+
+func (c Publishing) Generate(r *rand.Rand, _ int) reflect.Value {
+ var ok bool
+ var t reflect.Value
+
+ p := Publishing{}
+ //p.DeliveryMode = uint8(r.Intn(3))
+ //p.Priority = uint8(r.Intn(8))
+
+ if r.Intn(2) > 0 {
+ p.ContentType = "application/octet-stream"
+ }
+
+ if r.Intn(2) > 0 {
+ p.ContentEncoding = "gzip"
+ }
+
+ if r.Intn(2) > 0 {
+ p.CorrelationId = fmt.Sprintf("%d", r.Int())
+ }
+
+ if r.Intn(2) > 0 {
+ p.ReplyTo = fmt.Sprintf("%d", r.Int())
+ }
+
+ if r.Intn(2) > 0 {
+ p.MessageId = fmt.Sprintf("%d", r.Int())
+ }
+
+ if r.Intn(2) > 0 {
+ p.Type = fmt.Sprintf("%d", r.Int())
+ }
+
+ if r.Intn(2) > 0 {
+ p.AppId = fmt.Sprintf("%d", r.Int())
+ }
+
+ if r.Intn(2) > 0 {
+ p.Timestamp = time.Unix(r.Int63(), r.Int63())
+ }
+
+ if t, ok = quick.Value(reflect.TypeOf(p.Body), r); ok {
+ p.Body = t.Bytes()
+ }
+
+ return reflect.ValueOf(p)
+}
+
+func TestQuickPublishOnly(t *testing.T) {
+ if c := integrationConnection(t, "quick"); c != nil {
+ defer c.Close()
+ pub, err := c.Channel()
+ queue := "test-publish"
+
+ if _, err = pub.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Errorf("Failed to declare: %s", err)
+ return
+ }
+
+ defer pub.QueueDelete(queue, false, false, false)
+
+ quick.Check(func(msg Publishing) bool {
+ return pub.Publish("", queue, false, false, msg) == nil
+ }, nil)
+ }
+}
+
+func TestPublishEmptyBody(t *testing.T) {
+ c := integrationConnection(t, "empty")
+ if c != nil {
+ defer c.Close()
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Errorf("Failed to create channel")
+ return
+ }
+
+ queue := "test-TestPublishEmptyBody"
+
+ if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Fatalf("Could not declare")
+ }
+
+ messages, err := ch.Consume(queue, "", false, false, false, false, nil)
+ if err != nil {
+ t.Fatalf("Could not consume")
+ }
+
+ err = ch.Publish("", queue, false, false, Publishing{})
+ if err != nil {
+ t.Fatalf("Could not publish")
+ }
+
+ select {
+ case msg := <-messages:
+ if len(msg.Body) != 0 {
+ t.Errorf("Received non empty body")
+ }
+ case <-time.After(200 * time.Millisecond):
+ t.Errorf("Timeout on receive")
+ }
+ }
+}
+
+func TestPublishEmptyBodyWithHeadersIssue67(t *testing.T) {
+ c := integrationConnection(t, "issue67")
+ if c != nil {
+ defer c.Close()
+
+ ch, err := c.Channel()
+ if err != nil {
+ t.Errorf("Failed to create channel")
+ return
+ }
+
+ queue := "test-TestPublishEmptyBodyWithHeaders"
+
+ if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Fatalf("Could not declare")
+ }
+
+ messages, err := ch.Consume(queue, "", false, false, false, false, nil)
+ if err != nil {
+ t.Fatalf("Could not consume")
+ }
+
+ headers := Table{
+ "ham": "spam",
+ }
+
+ err = ch.Publish("", queue, false, false, Publishing{Headers: headers})
+ if err != nil {
+ t.Fatalf("Could not publish")
+ }
+
+ select {
+ case msg := <-messages:
+ if msg.Headers["ham"] == nil {
+ t.Fatalf("Headers aren't sent")
+ }
+ if msg.Headers["ham"] != "spam" {
+ t.Fatalf("Headers are wrong")
+ }
+ case <-time.After(200 * time.Millisecond):
+ t.Errorf("Timeout on receive")
+ }
+ }
+}
+
+func TestQuickPublishConsumeOnly(t *testing.T) {
+ c1 := integrationConnection(t, "quick-pub")
+ c2 := integrationConnection(t, "quick-sub")
+
+ if c1 != nil && c2 != nil {
+ defer c1.Close()
+ defer c2.Close()
+
+ pub, err := c1.Channel()
+ sub, err := c2.Channel()
+
+ queue := "TestPublishConsumeOnly"
+
+ if _, err = pub.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Errorf("Failed to declare: %s", err)
+ return
+ }
+
+ if _, err = sub.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Errorf("Failed to declare: %s", err)
+ return
+ }
+
+ defer sub.QueueDelete(queue, false, false, false)
+
+ ch, err := sub.Consume(queue, "", false, false, false, false, nil)
+ if err != nil {
+ t.Errorf("Could not sub: %s", err)
+ }
+
+ quick.CheckEqual(
+ func(msg Publishing) []byte {
+ empty := Publishing{Body: msg.Body}
+ if pub.Publish("", queue, false, false, empty) != nil {
+ return []byte{'X'}
+ }
+ return msg.Body
+ },
+ func(msg Publishing) []byte {
+ out := <-ch
+ out.Ack(false)
+ return out.Body
+ },
+ nil)
+ }
+}
+
+func TestQuickPublishConsumeBigBody(t *testing.T) {
+ c1 := integrationConnection(t, "big-pub")
+ c2 := integrationConnection(t, "big-sub")
+
+ if c1 != nil && c2 != nil {
+ defer c1.Close()
+ defer c2.Close()
+
+ pub, err := c1.Channel()
+ sub, err := c2.Channel()
+
+ queue := "test-pubsub"
+
+ if _, err = sub.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Errorf("Failed to declare: %s", err)
+ return
+ }
+
+ ch, err := sub.Consume(queue, "", false, false, false, false, nil)
+ if err != nil {
+ t.Errorf("Could not sub: %s", err)
+ }
+
+ fixture := Publishing{
+ Body: make([]byte, 1e4+1000),
+ }
+
+ if _, err = pub.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Errorf("Failed to declare: %s", err)
+ return
+ }
+
+ err = pub.Publish("", queue, false, false, fixture)
+ if err != nil {
+ t.Errorf("Could not publish big body")
+ }
+
+ select {
+ case msg := <-ch:
+ if bytes.Compare(msg.Body, fixture.Body) != 0 {
+ t.Errorf("Consumed big body didn't match")
+ }
+ case <-time.After(200 * time.Millisecond):
+ t.Errorf("Timeout on receive")
+ }
+ }
+}
+
+func TestIntegrationGetOk(t *testing.T) {
+ if c := integrationConnection(t, "getok"); c != nil {
+ defer c.Close()
+
+ queue := "test.get-ok"
+ ch, _ := c.Channel()
+
+ ch.QueueDeclare(queue, false, true, false, false, nil)
+ ch.Publish("", queue, false, false, Publishing{Body: []byte("ok")})
+
+ msg, ok, err := ch.Get(queue, false)
+
+ if err != nil {
+ t.Fatalf("Failed get: %v", err)
+ }
+
+ if !ok {
+ t.Fatalf("Get on a queued message did not find the message")
+ }
+
+ if string(msg.Body) != "ok" {
+ t.Fatalf("Get did not get the correct message")
+ }
+ }
+}
+
+func TestIntegrationGetEmpty(t *testing.T) {
+ if c := integrationConnection(t, "getok"); c != nil {
+ defer c.Close()
+
+ queue := "test.get-ok"
+ ch, _ := c.Channel()
+
+ ch.QueueDeclare(queue, false, true, false, false, nil)
+
+ _, ok, err := ch.Get(queue, false)
+
+ if err != nil {
+ t.Fatalf("Failed get: %v", err)
+ }
+
+ if !ok {
+ t.Fatalf("Get on a queued message retrieved a message when it shouldn't have")
+ }
+ }
+}
+
+func TestIntegrationTxCommit(t *testing.T) {
+ if c := integrationConnection(t, "txcommit"); c != nil {
+ defer c.Close()
+
+ queue := "test.tx.commit"
+ ch, _ := c.Channel()
+
+ ch.QueueDeclare(queue, false, true, false, false, nil)
+
+ if err := ch.Tx(); err != nil {
+ t.Fatalf("tx.select failed")
+ }
+
+ ch.Publish("", queue, false, false, Publishing{Body: []byte("ok")})
+
+ if err := ch.TxCommit(); err != nil {
+ t.Fatalf("tx.commit failed")
+ }
+
+ msg, ok, err := ch.Get(queue, false)
+
+ if err != nil || !ok {
+ t.Fatalf("Failed get: %v", err)
+ }
+
+ if string(msg.Body) != "ok" {
+ t.Fatalf("Get did not get the correct message from the transaction")
+ }
+ }
+}
+
+func TestIntegrationTxRollback(t *testing.T) {
+ if c := integrationConnection(t, "txrollback"); c != nil {
+ defer c.Close()
+
+ queue := "test.tx.rollback"
+ ch, _ := c.Channel()
+
+ ch.QueueDeclare(queue, false, true, false, false, nil)
+
+ if err := ch.Tx(); err != nil {
+ t.Fatalf("tx.select failed")
+ }
+
+ ch.Publish("", queue, false, false, Publishing{Body: []byte("ok")})
+
+ if err := ch.TxRollback(); err != nil {
+ t.Fatalf("tx.rollback failed")
+ }
+
+ _, ok, err := ch.Get(queue, false)
+
+ if err != nil {
+ t.Fatalf("Failed get: %v", err)
+ }
+
+ if ok {
+ t.Fatalf("message was published when it should have been rolled back")
+ }
+ }
+}
+
+func TestIntegrationReturn(t *testing.T) {
+ if c, ch := integrationQueue(t, "return"); c != nil {
+ defer c.Close()
+
+ ret := make(chan Return, 1)
+
+ ch.NotifyReturn(ret)
+
+ // mandatory publish to an exchange without a binding should be returned
+ ch.Publish("", "return-without-binding", true, false, Publishing{Body: []byte("mandatory")})
+
+ select {
+ case res := <-ret:
+ if string(res.Body) != "mandatory" {
+ t.Fatalf("expected return of the same message")
+ }
+
+ if res.ReplyCode != NoRoute {
+ t.Fatalf("expected no consumers reply code on the Return result, got: %v", res.ReplyCode)
+ }
+
+ case <-time.After(200 * time.Millisecond):
+ t.Fatalf("no return was received within 200ms")
+ }
+ }
+}
+
+func TestIntegrationCancel(t *testing.T) {
+ queue := "cancel"
+ consumerTag := "test.cancel"
+
+ if c, ch := integrationQueue(t, queue); c != nil {
+ defer c.Close()
+
+ cancels := ch.NotifyCancel(make(chan string, 1))
+
+ go func() {
+ if _, err := ch.Consume(queue, consumerTag, false, false, false, false, nil); err != nil {
+ t.Fatalf("cannot consume from %q to test NotifyCancel: %v", queue, err)
+ }
+ if _, err := ch.QueueDelete(queue, false, false, false); err != nil {
+ t.Fatalf("cannot delete integration queue: %v", err)
+ }
+ }()
+
+ select {
+ case tag := <-cancels:
+ if want, got := consumerTag, tag; want != got {
+ t.Fatalf("expected to be notified of deleted queue with consumer tag, got: %q", got)
+ }
+ case <-time.After(200 * time.Millisecond):
+ t.Fatalf("expected to be notified of deleted queue with 200ms")
+ }
+ }
+}
+
+func TestIntegrationConfirm(t *testing.T) {
+ if c, ch := integrationQueue(t, "confirm"); c != nil {
+ defer c.Close()
+
+ confirms := ch.NotifyPublish(make(chan Confirmation, 1))
+
+ if err := ch.Confirm(false); err != nil {
+ t.Fatalf("could not confirm")
+ }
+
+ ch.Publish("", "confirm", false, false, Publishing{Body: []byte("confirm")})
+
+ select {
+ case confirmed := <-confirms:
+ if confirmed.DeliveryTag != 1 {
+ t.Fatalf("expected ack starting with delivery tag of 1")
+ }
+ case <-time.After(200 * time.Millisecond):
+ t.Fatalf("no ack was received within 200ms")
+ }
+ }
+}
+
+// https://github.com/streadway/amqp/issues/61
+func TestRoundTripAllFieldValueTypes61(t *testing.T) {
+ if conn := integrationConnection(t, "issue61"); conn != nil {
+ defer conn.Close()
+ timestamp := time.Unix(100000000, 0)
+
+ headers := Table{
+ "A": []interface{}{
+ []interface{}{"nested array", int32(3)},
+ Decimal{2, 1},
+ Table{"S": "nested table in array"},
+ int32(2 << 20),
+ string("array string"),
+ timestamp,
+ nil,
+ byte(2),
+ float64(2.64),
+ float32(2.32),
+ int64(2 << 60),
+ int16(2 << 10),
+ bool(true),
+ []byte{'b', '2'},
+ },
+ "D": Decimal{1, 1},
+ "F": Table{"S": "nested table in table"},
+ "I": int32(1 << 20),
+ "S": string("string"),
+ "T": timestamp,
+ "V": nil,
+ "b": byte(1),
+ "d": float64(1.64),
+ "f": float32(1.32),
+ "l": int64(1 << 60),
+ "s": int16(1 << 10),
+ "t": bool(true),
+ "x": []byte{'b', '1'},
+ }
+
+ queue := "test.issue61-roundtrip"
+ ch, _ := conn.Channel()
+
+ if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Fatalf("Could not declare")
+ }
+
+ msgs, err := ch.Consume(queue, "", false, false, false, false, nil)
+ if err != nil {
+ t.Fatalf("Could not consume")
+ }
+
+ err = ch.Publish("", queue, false, false, Publishing{Body: []byte("ignored"), Headers: headers})
+ if err != nil {
+ t.Fatalf("Could not publish: %v", err)
+ }
+
+ msg, ok := <-msgs
+
+ if !ok {
+ t.Fatalf("Channel closed prematurely likely due to publish exception")
+ }
+
+ for k, v := range headers {
+ if !reflect.DeepEqual(v, msg.Headers[k]) {
+ t.Errorf("Round trip header not the same for key %q: expected: %#v, got %#v", k, v, msg.Headers[k])
+ }
+ }
+ }
+}
+
+// Declares a queue with the x-message-ttl extension to exercise integer
+// serialization.
+//
+// Relates to https://github.com/streadway/amqp/issues/60
+//
+func TestDeclareArgsXMessageTTL(t *testing.T) {
+ if conn := integrationConnection(t, "declareTTL"); conn != nil {
+ defer conn.Close()
+
+ ch, _ := conn.Channel()
+ args := Table{"x-message-ttl": int32(9000000)}
+
+ // should not drop the connection
+ if _, err := ch.QueueDeclare("declareWithTTL", false, true, false, false, args); err != nil {
+ t.Fatalf("cannot declare with TTL: got: %v", err)
+ }
+ }
+}
+
+// Sets up the topology where rejected messages will be forwarded
+// to a fanout exchange, with a single queue bound.
+//
+// Relates to https://github.com/streadway/amqp/issues/56
+//
+func TestDeclareArgsRejectToDeadLetterQueue(t *testing.T) {
+ if conn := integrationConnection(t, "declareArgs"); conn != nil {
+ defer conn.Close()
+
+ ex, q := "declareArgs", "declareArgs-deliveries"
+ dlex, dlq := ex+"-dead-letter", q+"-dead-letter"
+
+ ch, _ := conn.Channel()
+
+ if err := ch.ExchangeDeclare(ex, "fanout", false, true, false, false, nil); err != nil {
+ t.Fatalf("cannot declare %v: got: %v", ex, err)
+ }
+
+ if err := ch.ExchangeDeclare(dlex, "fanout", false, true, false, false, nil); err != nil {
+ t.Fatalf("cannot declare %v: got: %v", dlex, err)
+ }
+
+ if _, err := ch.QueueDeclare(dlq, false, true, false, false, nil); err != nil {
+ t.Fatalf("cannot declare %v: got: %v", dlq, err)
+ }
+
+ if err := ch.QueueBind(dlq, "#", dlex, false, nil); err != nil {
+ t.Fatalf("cannot bind %v to %v: got: %v", dlq, dlex, err)
+ }
+
+ if _, err := ch.QueueDeclare(q, false, true, false, false, Table{
+ "x-dead-letter-exchange": dlex,
+ }); err != nil {
+ t.Fatalf("cannot declare %v with dlq %v: got: %v", q, dlex, err)
+ }
+
+ if err := ch.QueueBind(q, "#", ex, false, nil); err != nil {
+ t.Fatalf("cannot bind %v: got: %v", ex, err)
+ }
+
+ fails, err := ch.Consume(q, "", false, false, false, false, nil)
+ if err != nil {
+ t.Fatalf("cannot consume %v: got: %v", q, err)
+ }
+
+ // Reject everything consumed
+ go func() {
+ for d := range fails {
+ d.Reject(false)
+ }
+ }()
+
+ // Publish the 'poison'
+ if err := ch.Publish(ex, q, true, false, Publishing{Body: []byte("ignored")}); err != nil {
+ t.Fatalf("publishing failed")
+ }
+
+ // spin-get until message arrives on the dead-letter queue with a
+ // synchronous parse to exercise the array field (x-death) set by the
+ // server relating to issue-56
+ for i := 0; i < 10; i++ {
+ d, got, err := ch.Get(dlq, false)
+ if !got && err == nil {
+ continue
+ } else if err != nil {
+ t.Fatalf("expected success in parsing reject, got: %v", err)
+ } else {
+ // pass if we've parsed an array
+ if v, ok := d.Headers["x-death"]; ok {
+ if _, ok := v.([]interface{}); ok {
+ return
+ }
+ }
+ t.Fatalf("array field x-death expected in the headers, got: %v (%T)", d.Headers, d.Headers["x-death"])
+ }
+ }
+
+ t.Fatalf("expectd dead-letter after 10 get attempts")
+ }
+}
+
+// https://github.com/streadway/amqp/issues/48
+func TestDeadlockConsumerIssue48(t *testing.T) {
+ if conn := integrationConnection(t, "issue48"); conn != nil {
+ defer conn.Close()
+
+ deadline := make(chan bool)
+ go func() {
+ select {
+ case <-time.After(5 * time.Second):
+ panic("expected to receive 2 deliveries while in an RPC, got a deadlock")
+ case <-deadline:
+ // pass
+ }
+ }()
+
+ ch, err := conn.Channel()
+ if err != nil {
+ t.Fatalf("got error on channel.open: %v", err)
+ }
+
+ queue := "test-issue48"
+
+ if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Fatalf("expected to declare a queue: %v", err)
+ }
+
+ if err := ch.Confirm(false); err != nil {
+ t.Fatalf("got error on confirm: %v", err)
+ }
+
+ confirms := ch.NotifyPublish(make(chan Confirmation, 2))
+
+ for i := 0; i < cap(confirms); i++ {
+ // Fill the queue with some new or remaining publishings
+ ch.Publish("", queue, false, false, Publishing{Body: []byte("")})
+ }
+
+ for i := 0; i < cap(confirms); i++ {
+ // Wait for them to land on the queue so they'll be delivered on consume
+ <-confirms
+ }
+
+ // Consuming should send them all on the wire
+ msgs, err := ch.Consume(queue, "", false, false, false, false, nil)
+ if err != nil {
+ t.Fatalf("got error on consume: %v", err)
+ }
+
+ // We pop one off the chan, the other is on the wire
+ <-msgs
+
+ // Opening a new channel (any RPC) while another delivery is on the wire
+ if _, err := conn.Channel(); err != nil {
+ t.Fatalf("got error on consume: %v", err)
+ }
+
+ // We pop the next off the chan
+ <-msgs
+
+ deadline <- true
+ }
+}
+
+// https://github.com/streadway/amqp/issues/46
+func TestRepeatedChannelExceptionWithPublishAndMaxProcsIssue46(t *testing.T) {
+ conn := integrationConnection(t, "issue46")
+ if conn != nil {
+ for i := 0; i < 100; i++ {
+ ch, err := conn.Channel()
+ if err != nil {
+ t.Fatalf("expected error only on publish, got error on channel.open: %v", err)
+ }
+
+ for j := 0; j < 10; j++ {
+ err = ch.Publish("not-existing-exchange", "some-key", false, false, Publishing{Body: []byte("some-data")})
+ if err, ok := err.(Error); ok {
+ if err.Code != 504 {
+ t.Fatalf("expected channel only exception, got: %v", err)
+ }
+ }
+ }
+ }
+ }
+}
+
+// https://github.com/streadway/amqp/issues/43
+func TestChannelExceptionWithCloseIssue43(t *testing.T) {
+ conn := integrationConnection(t, "issue43")
+ if conn != nil {
+ go func() {
+ for err := range conn.NotifyClose(make(chan *Error)) {
+ t.Log(err.Error())
+ }
+ }()
+
+ c1, err := conn.Channel()
+ if err != nil {
+ panic(err)
+ }
+
+ go func() {
+ for err := range c1.NotifyClose(make(chan *Error)) {
+ t.Log("Channel1 Close: " + err.Error())
+ }
+ }()
+
+ c2, err := conn.Channel()
+ if err != nil {
+ panic(err)
+ }
+
+ go func() {
+ for err := range c2.NotifyClose(make(chan *Error)) {
+ t.Log("Channel2 Close: " + err.Error())
+ }
+ }()
+
+ // Cause an asynchronous channel exception causing the server
+ // to send a "channel.close" method either before or after the next
+ // asynchronous method.
+ err = c1.Publish("nonexisting-exchange", "", false, false, Publishing{})
+ if err != nil {
+ panic(err)
+ }
+
+ // Receive or send the channel close method, the channel shuts down
+ // but this expects a channel.close-ok to be received.
+ c1.Close()
+
+ // This ensures that the 2nd channel is unaffected by the channel exception
+ // on channel 1.
+ err = c2.ExchangeDeclare("test-channel-still-exists", "direct", false, true, false, false, nil)
+ if err != nil {
+ panic(err)
+ }
+ }
+}
+
+// https://github.com/streadway/amqp/issues/7
+func TestCorruptedMessageIssue7(t *testing.T) {
+ messageCount := 1024
+
+ c1 := integrationConnection(t, "")
+ c2 := integrationConnection(t, "")
+
+ if c1 != nil && c2 != nil {
+ defer c1.Close()
+ defer c2.Close()
+
+ pub, err := c1.Channel()
+ if err != nil {
+ t.Fatalf("Cannot create Channel")
+ }
+
+ sub, err := c2.Channel()
+ if err != nil {
+ t.Fatalf("Cannot create Channel")
+ }
+
+ queue := "test-corrupted-message-regression"
+
+ if _, err := pub.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Fatalf("Cannot declare")
+ }
+
+ if _, err := sub.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Fatalf("Cannot declare")
+ }
+
+ msgs, err := sub.Consume(queue, "", false, false, false, false, nil)
+ if err != nil {
+ t.Fatalf("Cannot consume")
+ }
+
+ for i := 0; i < messageCount; i++ {
+ err := pub.Publish("", queue, false, false, Publishing{
+ Body: generateCrc32Random(7 * i),
+ })
+
+ if err != nil {
+ t.Fatalf("Failed to publish")
+ }
+ }
+
+ for i := 0; i < messageCount; i++ {
+ select {
+ case msg := <-msgs:
+ assertMessageCrc32(t, msg.Body, fmt.Sprintf("missed match at %d", i))
+ case <-time.After(200 * time.Millisecond):
+ t.Fatalf("Timeout on recv")
+ }
+ }
+ }
+}
+
+// https://github.com/streadway/amqp/issues/136
+func TestChannelCounterShouldNotPanicIssue136(t *testing.T) {
+ if c := integrationConnection(t, "issue136"); c != nil {
+ defer c.Close()
+ var wg sync.WaitGroup
+
+ // exceeds 65535 channels
+ for i := 0; i < 8; i++ {
+ wg.Add(1)
+ go func(i int) {
+ for j := 0; j < 10000; j++ {
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("failed to create channel %d:%d, got: %v", i, j, err)
+ }
+ if err := ch.Close(); err != nil {
+ t.Fatalf("failed to close channel %d:%d, got: %v", i, j, err)
+ }
+ }
+ wg.Done()
+ }(i)
+ }
+ wg.Wait()
+ }
+}
+
+func TestExchangeDeclarePrecondition(t *testing.T) {
+ c1 := integrationConnection(t, "exchange-double-declare")
+ c2 := integrationConnection(t, "exchange-double-declare-cleanup")
+ if c1 != nil && c2 != nil {
+ defer c1.Close()
+ defer c2.Close()
+
+ ch, err := c1.Channel()
+ if err != nil {
+ t.Fatalf("Create channel")
+ }
+
+ exchange := "test-mismatched-redeclare"
+
+ err = ch.ExchangeDeclare(
+ exchange,
+ "direct", // exchangeType
+ false, // durable
+ true, // auto-delete
+ false, // internal
+ false, // noWait
+ nil, // arguments
+ )
+ if err != nil {
+ t.Fatalf("Could not initially declare exchange")
+ }
+
+ err = ch.ExchangeDeclare(
+ exchange,
+ "direct",
+ true, // different durability
+ true,
+ false,
+ false,
+ nil,
+ )
+
+ if err == nil {
+ t.Fatalf("Expected to fail a redeclare with different durability, didn't receive an error")
+ }
+
+ if err, ok := err.(Error); ok {
+ if err.Code != PreconditionFailed {
+ t.Fatalf("Expected precondition error")
+ }
+ if !err.Recover {
+ t.Fatalf("Expected to be able to recover")
+ }
+ }
+
+ ch2, _ := c2.Channel()
+ if err = ch2.ExchangeDelete(exchange, false, false); err != nil {
+ t.Fatalf("Could not delete exchange: %v", err)
+ }
+ }
+}
+
+func TestRabbitMQQueueTTLGet(t *testing.T) {
+ if c := integrationRabbitMQ(t, "ttl"); c != nil {
+ defer c.Close()
+
+ queue := "test.rabbitmq-message-ttl"
+ channel, err := c.Channel()
+ if err != nil {
+ t.Fatalf("channel: %v", err)
+ }
+
+ if _, err = channel.QueueDeclare(
+ queue,
+ false,
+ true,
+ false,
+ false,
+ Table{"x-message-ttl": int32(100)}, // in ms
+ ); err != nil {
+ t.Fatalf("queue declare: %s", err)
+ }
+
+ channel.Publish("", queue, false, false, Publishing{Body: []byte("ttl")})
+
+ time.Sleep(200 * time.Millisecond)
+
+ _, ok, err := channel.Get(queue, false)
+
+ if ok {
+ t.Fatalf("Expected the message to expire in 100ms, it didn't expire after 200ms")
+ }
+
+ if err != nil {
+ t.Fatalf("Failed to get on ttl queue")
+ }
+ }
+}
+
+func TestRabbitMQQueueNackMultipleRequeue(t *testing.T) {
+ if c := integrationRabbitMQ(t, "nack"); c != nil {
+ defer c.Close()
+
+ if c.isCapable("basic.nack") {
+ queue := "test.rabbitmq-basic-nack"
+ channel, err := c.Channel()
+ if err != nil {
+ t.Fatalf("channel: %v", err)
+ }
+
+ if _, err = channel.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Fatalf("queue declare: %s", err)
+ }
+
+ channel.Publish("", queue, false, false, Publishing{Body: []byte("1")})
+ channel.Publish("", queue, false, false, Publishing{Body: []byte("2")})
+
+ m1, ok, err := channel.Get(queue, false)
+ if !ok || err != nil || m1.Body[0] != '1' {
+ t.Fatalf("could not get message %v", m1)
+ }
+
+ m2, ok, err := channel.Get(queue, false)
+ if !ok || err != nil || m2.Body[0] != '2' {
+ t.Fatalf("could not get message %v", m2)
+ }
+
+ m2.Nack(true, true)
+
+ m1, ok, err = channel.Get(queue, false)
+ if !ok || err != nil || m1.Body[0] != '1' {
+ t.Fatalf("could not get message %v", m1)
+ }
+
+ m2, ok, err = channel.Get(queue, false)
+ if !ok || err != nil || m2.Body[0] != '2' {
+ t.Fatalf("could not get message %v", m2)
+ }
+ }
+ }
+}
+
+func TestConsumerCancelNotification(t *testing.T) {
+ c := integrationConnection(t, "consumer cancel notification")
+ if c != nil {
+ defer c.Close()
+ ch, err := c.Channel()
+ if err != nil {
+ t.Fatalf("got error on channel.open: %v", err)
+ }
+
+ queue := "test-consumer-cancel-notification"
+
+ if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ t.Fatalf("expected to declare a queue: %v", err)
+ }
+
+ if _, err := ch.Consume(queue, "", false, false, false, false, nil); err != nil {
+ t.Fatalf("basic.consume failed")
+ }
+ // consumer cancel notification channel
+ ccnChan := make(chan string, 1)
+ ch.NotifyCancel(ccnChan)
+
+ if _, err := ch.QueueDelete(queue, false, false, true); err != nil {
+ t.Fatalf("queue.delete failed: %s", err)
+ }
+
+ select {
+ case <-ccnChan:
+ // do nothing
+ case <-time.After(time.Second * 10):
+ t.Errorf("basic.cancel wasn't received")
+ t.Fail()
+ }
+ // we don't close ccnChan because channel shutdown
+ // does it
+ }
+}
+
+/*
+ * Support for integration tests
+ */
+
+func integrationURLFromEnv() string {
+ url := os.Getenv("AMQP_URL")
+ if url == "" {
+ url = "amqp://"
+ }
+ return url
+}
+
+func loggedConnection(t *testing.T, conn *Connection, name string) *Connection {
+ if name != "" {
+ conn.conn = &logIO{t, name, conn.conn}
+ }
+ return conn
+}
+
+// Returns a connection to the AMQP if the AMQP_URL environment
+// variable is set and a connection can be established.
+func integrationConnection(t *testing.T, name string) *Connection {
+ conn, err := Dial(integrationURLFromEnv())
+ if err != nil {
+ t.Errorf("dial integration server: %s", err)
+ return nil
+ }
+ return loggedConnection(t, conn, name)
+}
+
+// Returns a connection, channel and delcares a queue when the AMQP_URL is in the environment
+func integrationQueue(t *testing.T, name string) (*Connection, *Channel) {
+ if conn := integrationConnection(t, name); conn != nil {
+ if channel, err := conn.Channel(); err == nil {
+ if _, err = channel.QueueDeclare(name, false, true, false, false, nil); err == nil {
+ return conn, channel
+ }
+ }
+ }
+ return nil, nil
+}
+
+// Delegates to integrationConnection and only returns a connection if the
+// product is RabbitMQ
+func integrationRabbitMQ(t *testing.T, name string) *Connection {
+ if conn := integrationConnection(t, "connect"); conn != nil {
+ if server, ok := conn.Properties["product"]; ok && server == "RabbitMQ" {
+ return conn
+ }
+ }
+
+ return nil
+}
+
+func assertConsumeBody(t *testing.T, messages <-chan Delivery, want []byte) (msg *Delivery) {
+ select {
+ case got := <-messages:
+ if bytes.Compare(want, got.Body) != 0 {
+ t.Fatalf("Message body does not match want: %v, got: %v, for: %+v", want, got.Body, got)
+ }
+ msg = &got
+ case <-time.After(200 * time.Millisecond):
+ t.Fatalf("Timeout waiting for %v", want)
+ }
+
+ return msg
+}
+
+// Pulls out the CRC and verifies the remaining content against the CRC
+func assertMessageCrc32(t *testing.T, msg []byte, assert string) {
+ size := binary.BigEndian.Uint32(msg[:4])
+
+ crc := crc32.NewIEEE()
+ crc.Write(msg[8:])
+
+ if binary.BigEndian.Uint32(msg[4:8]) != crc.Sum32() {
+ t.Fatalf("Message does not match CRC: %s", assert)
+ }
+
+ if int(size) != len(msg)-8 {
+ t.Fatalf("Message does not match size, should=%d, is=%d: %s", size, len(msg)-8, assert)
+ }
+}
+
+// Creates a random body size with a leading 32-bit CRC in network byte order
+// that verifies the remaining slice
+func generateCrc32Random(size int) []byte {
+ msg := make([]byte, size+8)
+ if _, err := io.ReadFull(devrand.Reader, msg); err != nil {
+ panic(err)
+ }
+
+ crc := crc32.NewIEEE()
+ crc.Write(msg[8:])
+
+ binary.BigEndian.PutUint32(msg[0:4], uint32(size))
+ binary.BigEndian.PutUint32(msg[4:8], crc.Sum32())
+
+ return msg
+}
diff --git a/vendor/github.com/streadway/amqp/pre-commit b/vendor/github.com/streadway/amqp/pre-commit
new file mode 100755
index 00000000..7607f467
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/pre-commit
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+GOFMT_FILES=$(gofmt -l .)
+if [ -n "${GOFMT_FILES}" ]; then
+ printf >&2 'gofmt failed for the following files:\n%s\n\nplease run "gofmt -w ." on your changes before committing.\n' "${GOFMT_FILES}"
+ exit 1
+fi
+
+GOLINT_ERRORS=$(golint ./... | grep -v "Id should be")
+if [ -n "${GOLINT_ERRORS}" ]; then
+ printf >&2 'golint failed for the following reasons:\n%s\n\nplease run 'golint ./...' on your changes before committing.\n' "${GOLINT_ERRORS}"
+ exit 1
+fi
+
+GOVET_ERRORS=$(go tool vet *.go 2>&1)
+if [ -n "${GOVET_ERRORS}" ]; then
+ printf >&2 'go vet failed for the following reasons:\n%s\n\nplease run "go tool vet *.go" on your changes before committing.\n' "${GOVET_ERRORS}"
+ exit 1
+fi
+
+if [ -z "${NOTEST}" ]; then
+ printf >&2 'Running short tests...\n'
+ env AMQP_URL= go test -short -v | egrep 'PASS|ok'
+
+ if [ $? -ne 0 ]; then
+ printf >&2 'go test failed, please fix before committing.\n'
+ exit 1
+ fi
+fi
diff --git a/vendor/github.com/streadway/amqp/read.go b/vendor/github.com/streadway/amqp/read.go
new file mode 100644
index 00000000..7eb8675e
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/read.go
@@ -0,0 +1,451 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "io"
+ "time"
+)
+
+/*
+Reads a frame from an input stream and returns an interface that can be cast into
+one of the following:
+
+ methodFrame
+ PropertiesFrame
+ bodyFrame
+ heartbeatFrame
+
+2.3.5 frame Details
+
+All frames consist of a header (7 octets), a payload of arbitrary size, and a
+'frame-end' octet that detects malformed frames:
+
+ 0 1 3 7 size+7 size+8
+ +------+---------+-------------+ +------------+ +-----------+
+ | type | channel | size | | payload | | frame-end |
+ +------+---------+-------------+ +------------+ +-----------+
+ octet short long size octets octet
+
+To read a frame, we:
+ 1. Read the header and check the frame type and channel.
+ 2. Depending on the frame type, we read the payload and process it.
+ 3. Read the frame end octet.
+
+In realistic implementations where performance is a concern, we would use
+“read-ahead buffering” or
+
+“gathering reads” to avoid doing three separate system calls to read a frame.
+*/
+func (r *reader) ReadFrame() (frame frame, err error) {
+ var scratch [7]byte
+
+ if _, err = io.ReadFull(r.r, scratch[:7]); err != nil {
+ return
+ }
+
+ typ := uint8(scratch[0])
+ channel := binary.BigEndian.Uint16(scratch[1:3])
+ size := binary.BigEndian.Uint32(scratch[3:7])
+
+ switch typ {
+ case frameMethod:
+ if frame, err = r.parseMethodFrame(channel, size); err != nil {
+ return
+ }
+
+ case frameHeader:
+ if frame, err = r.parseHeaderFrame(channel, size); err != nil {
+ return
+ }
+
+ case frameBody:
+ if frame, err = r.parseBodyFrame(channel, size); err != nil {
+ return nil, err
+ }
+
+ case frameHeartbeat:
+ if frame, err = r.parseHeartbeatFrame(channel, size); err != nil {
+ return
+ }
+
+ default:
+ return nil, ErrFrame
+ }
+
+ if _, err = io.ReadFull(r.r, scratch[:1]); err != nil {
+ return nil, err
+ }
+
+ if scratch[0] != frameEnd {
+ return nil, ErrFrame
+ }
+
+ return
+}
+
+func readShortstr(r io.Reader) (v string, err error) {
+ var length uint8
+ if err = binary.Read(r, binary.BigEndian, &length); err != nil {
+ return
+ }
+
+ bytes := make([]byte, length)
+ if _, err = io.ReadFull(r, bytes); err != nil {
+ return
+ }
+ return string(bytes), nil
+}
+
+func readLongstr(r io.Reader) (v string, err error) {
+ var length uint32
+ if err = binary.Read(r, binary.BigEndian, &length); err != nil {
+ return
+ }
+
+ bytes := make([]byte, length)
+ if _, err = io.ReadFull(r, bytes); err != nil {
+ return
+ }
+ return string(bytes), nil
+}
+
+func readDecimal(r io.Reader) (v Decimal, err error) {
+ if err = binary.Read(r, binary.BigEndian, &v.Scale); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &v.Value); err != nil {
+ return
+ }
+ return
+}
+
+func readFloat32(r io.Reader) (v float32, err error) {
+ if err = binary.Read(r, binary.BigEndian, &v); err != nil {
+ return
+ }
+ return
+}
+
+func readFloat64(r io.Reader) (v float64, err error) {
+ if err = binary.Read(r, binary.BigEndian, &v); err != nil {
+ return
+ }
+ return
+}
+
+func readTimestamp(r io.Reader) (v time.Time, err error) {
+ var sec int64
+ if err = binary.Read(r, binary.BigEndian, &sec); err != nil {
+ return
+ }
+ return time.Unix(sec, 0), nil
+}
+
+/*
+'A': []interface{}
+'D': Decimal
+'F': Table
+'I': int32
+'S': string
+'T': time.Time
+'V': nil
+'b': byte
+'d': float64
+'f': float32
+'l': int64
+'s': int16
+'t': bool
+'x': []byte
+*/
+func readField(r io.Reader) (v interface{}, err error) {
+ var typ byte
+ if err = binary.Read(r, binary.BigEndian, &typ); err != nil {
+ return
+ }
+
+ switch typ {
+ case 't':
+ var value uint8
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return (value != 0), nil
+
+ case 'b':
+ var value [1]byte
+ if _, err = io.ReadFull(r, value[0:1]); err != nil {
+ return
+ }
+ return value[0], nil
+
+ case 's':
+ var value int16
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'I':
+ var value int32
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'l':
+ var value int64
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'f':
+ var value float32
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'd':
+ var value float64
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'D':
+ return readDecimal(r)
+
+ case 'S':
+ return readLongstr(r)
+
+ case 'A':
+ return readArray(r)
+
+ case 'T':
+ return readTimestamp(r)
+
+ case 'F':
+ return readTable(r)
+
+ case 'x':
+ var len int32
+ if err = binary.Read(r, binary.BigEndian, &len); err != nil {
+ return nil, err
+ }
+
+ value := make([]byte, len)
+ if _, err = io.ReadFull(r, value); err != nil {
+ return nil, err
+ }
+ return value, err
+
+ case 'V':
+ return nil, nil
+ }
+
+ return nil, ErrSyntax
+}
+
+/*
+ Field tables are long strings that contain packed name-value pairs. The
+ name-value pairs are encoded as short string defining the name, and octet
+ defining the values type and then the value itself. The valid field types for
+ tables are an extension of the native integer, bit, string, and timestamp
+ types, and are shown in the grammar. Multi-octet integer fields are always
+ held in network byte order.
+*/
+func readTable(r io.Reader) (table Table, err error) {
+ var nested bytes.Buffer
+ var str string
+
+ if str, err = readLongstr(r); err != nil {
+ return
+ }
+
+ nested.Write([]byte(str))
+
+ table = make(Table)
+
+ for nested.Len() > 0 {
+ var key string
+ var value interface{}
+
+ if key, err = readShortstr(&nested); err != nil {
+ return
+ }
+
+ if value, err = readField(&nested); err != nil {
+ return
+ }
+
+ table[key] = value
+ }
+
+ return
+}
+
+func readArray(r io.Reader) ([]interface{}, error) {
+ var (
+ size uint32
+ err error
+ )
+
+ if err = binary.Read(r, binary.BigEndian, &size); err != nil {
+ return nil, err
+ }
+
+ var (
+ lim = &io.LimitedReader{R: r, N: int64(size)}
+ arr = []interface{}{}
+ field interface{}
+ )
+
+ for {
+ if field, err = readField(lim); err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, err
+ }
+ arr = append(arr, field)
+ }
+
+ return arr, nil
+}
+
+// Checks if this bit mask matches the flags bitset
+func hasProperty(mask uint16, prop int) bool {
+ return int(mask)&prop > 0
+}
+
+func (r *reader) parseHeaderFrame(channel uint16, size uint32) (frame frame, err error) {
+ hf := &headerFrame{
+ ChannelId: channel,
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &hf.ClassId); err != nil {
+ return
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &hf.weight); err != nil {
+ return
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &hf.Size); err != nil {
+ return
+ }
+
+ var flags uint16
+
+ if err = binary.Read(r.r, binary.BigEndian, &flags); err != nil {
+ return
+ }
+
+ if hasProperty(flags, flagContentType) {
+ if hf.Properties.ContentType, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagContentEncoding) {
+ if hf.Properties.ContentEncoding, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagHeaders) {
+ if hf.Properties.Headers, err = readTable(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagDeliveryMode) {
+ if err = binary.Read(r.r, binary.BigEndian, &hf.Properties.DeliveryMode); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagPriority) {
+ if err = binary.Read(r.r, binary.BigEndian, &hf.Properties.Priority); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagCorrelationId) {
+ if hf.Properties.CorrelationId, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagReplyTo) {
+ if hf.Properties.ReplyTo, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagExpiration) {
+ if hf.Properties.Expiration, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagMessageId) {
+ if hf.Properties.MessageId, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagTimestamp) {
+ if hf.Properties.Timestamp, err = readTimestamp(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagType) {
+ if hf.Properties.Type, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagUserId) {
+ if hf.Properties.UserId, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagAppId) {
+ if hf.Properties.AppId, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagReserved1) {
+ if hf.Properties.reserved1, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+
+ return hf, nil
+}
+
+func (r *reader) parseBodyFrame(channel uint16, size uint32) (frame frame, err error) {
+ bf := &bodyFrame{
+ ChannelId: channel,
+ Body: make([]byte, size),
+ }
+
+ if _, err = io.ReadFull(r.r, bf.Body); err != nil {
+ return nil, err
+ }
+
+ return bf, nil
+}
+
+var errHeartbeatPayload = errors.New("Heartbeats should not have a payload")
+
+func (r *reader) parseHeartbeatFrame(channel uint16, size uint32) (frame frame, err error) {
+ hf := &heartbeatFrame{
+ ChannelId: channel,
+ }
+
+ if size > 0 {
+ return nil, errHeartbeatPayload
+ }
+
+ return hf, nil
+}
diff --git a/vendor/github.com/streadway/amqp/read_test.go b/vendor/github.com/streadway/amqp/read_test.go
new file mode 100644
index 00000000..143e2624
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/read_test.go
@@ -0,0 +1,26 @@
+package amqp
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestGoFuzzCrashers(t *testing.T) {
+ if testing.Short() {
+ t.Skip("excessive allocation")
+ }
+
+ testData := []string{
+ "\b000000",
+ "\x02\x16\x10�[��\t\xbdui�" + "\x10\x01\x00\xff\xbf\xef\xbfサn\x99\x00\x10r",
+ "\x0300\x00\x00\x00\x040000",
+ }
+
+ for idx, testStr := range testData {
+ r := reader{strings.NewReader(testStr)}
+ frame, err := r.ReadFrame()
+ if err != nil && frame != nil {
+ t.Errorf("%d. frame is not nil: %#v err = %v", idx, frame, err)
+ }
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/reconnect_test.go b/vendor/github.com/streadway/amqp/reconnect_test.go
new file mode 100644
index 00000000..5a06cb7a
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/reconnect_test.go
@@ -0,0 +1,113 @@
+package amqp_test
+
+import (
+ "fmt"
+ "github.com/streadway/amqp"
+ "os"
+)
+
+// Every connection should declare the topology they expect
+func setup(url, queue string) (*amqp.Connection, *amqp.Channel, error) {
+ conn, err := amqp.Dial(url)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ ch, err := conn.Channel()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil {
+ return nil, nil, err
+ }
+
+ return conn, ch, nil
+}
+
+func consume(url, queue string) (*amqp.Connection, <-chan amqp.Delivery, error) {
+ conn, ch, err := setup(url, queue)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Indicate we only want 1 message to acknowledge at a time.
+ if err := ch.Qos(1, 0, false); err != nil {
+ return nil, nil, err
+ }
+
+ // Exclusive consumer
+ deliveries, err := ch.Consume(queue, "", false, true, false, false, nil)
+
+ return conn, deliveries, err
+}
+
+func ExampleConnection_reconnect() {
+ if url := os.Getenv("AMQP_URL"); url != "" {
+ queue := "example.reconnect"
+
+ // The connection/channel for publishing to interleave the ingress messages
+ // between reconnects, shares the same topology as the consumer. If we rather
+ // sent all messages up front, the first consumer would receive every message.
+ // We would rather show how the messages are not lost between reconnects.
+ _, pub, err := setup(url, queue)
+ if err != nil {
+ fmt.Println("err publisher setup:", err)
+ return
+ }
+
+ // Purge the queue from the publisher side to establish initial state
+ if _, err := pub.QueuePurge(queue, false); err != nil {
+ fmt.Println("err purge:", err)
+ return
+ }
+
+ // Reconnect simulation, should be for { ... } in production
+ for i := 1; i <= 3; i++ {
+ fmt.Println("connect")
+
+ conn, deliveries, err := consume(url, queue)
+ if err != nil {
+ fmt.Println("err consume:", err)
+ return
+ }
+
+ // Simulate a producer on a different connection showing that consumers
+ // continue where they were left off after each reconnect.
+ if err := pub.Publish("", queue, false, false, amqp.Publishing{
+ Body: []byte(fmt.Sprintf("%d", i)),
+ }); err != nil {
+ fmt.Println("err publish:", err)
+ return
+ }
+
+ // Simulates a consumer that when the range finishes, will setup a new
+ // session and begin ranging over the deliveries again.
+ for msg := range deliveries {
+ fmt.Println(string(msg.Body))
+ msg.Ack(false)
+
+ // Simulate an error like a server restart, loss of route or operator
+ // intervention that results in the connection terminating
+ go conn.Close()
+ }
+ }
+ } else {
+ // pass with expected output when not running in an integration
+ // environment.
+ fmt.Println("connect")
+ fmt.Println("1")
+ fmt.Println("connect")
+ fmt.Println("2")
+ fmt.Println("connect")
+ fmt.Println("3")
+ }
+
+ // Output:
+ // connect
+ // 1
+ // connect
+ // 2
+ // connect
+ // 3
+}
diff --git a/vendor/github.com/streadway/amqp/return.go b/vendor/github.com/streadway/amqp/return.go
new file mode 100644
index 00000000..10dcedb2
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/return.go
@@ -0,0 +1,64 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "time"
+)
+
+// Return captures a flattened struct of fields returned by the server when a
+// Publishing is unable to be delivered either due to the `mandatory` flag set
+// and no route found, or `immediate` flag set and no free consumer.
+type Return struct {
+ ReplyCode uint16 // reason
+ ReplyText string // description
+ Exchange string // basic.publish exchange
+ RoutingKey string // basic.publish routing key
+
+ // Properties
+ ContentType string // MIME content type
+ ContentEncoding string // MIME content encoding
+ Headers Table // Application or header exchange table
+ DeliveryMode uint8 // queue implementation use - non-persistent (1) or persistent (2)
+ Priority uint8 // queue implementation use - 0 to 9
+ CorrelationId string // application use - correlation identifier
+ ReplyTo string // application use - address to to reply to (ex: RPC)
+ Expiration string // implementation use - message expiration spec
+ MessageId string // application use - message identifier
+ Timestamp time.Time // application use - message timestamp
+ Type string // application use - message type name
+ UserId string // application use - creating user id
+ AppId string // application use - creating application
+
+ Body []byte
+}
+
+func newReturn(msg basicReturn) *Return {
+ props, body := msg.getContent()
+
+ return &Return{
+ ReplyCode: msg.ReplyCode,
+ ReplyText: msg.ReplyText,
+ Exchange: msg.Exchange,
+ RoutingKey: msg.RoutingKey,
+
+ Headers: props.Headers,
+ ContentType: props.ContentType,
+ ContentEncoding: props.ContentEncoding,
+ DeliveryMode: props.DeliveryMode,
+ Priority: props.Priority,
+ CorrelationId: props.CorrelationId,
+ ReplyTo: props.ReplyTo,
+ Expiration: props.Expiration,
+ MessageId: props.MessageId,
+ Timestamp: props.Timestamp,
+ Type: props.Type,
+ UserId: props.UserId,
+ AppId: props.AppId,
+
+ Body: body,
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/shared_test.go b/vendor/github.com/streadway/amqp/shared_test.go
new file mode 100644
index 00000000..669a50a3
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/shared_test.go
@@ -0,0 +1,71 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "encoding/hex"
+ "io"
+ "testing"
+)
+
+type pipe struct {
+ r *io.PipeReader
+ w *io.PipeWriter
+}
+
+func (p pipe) Read(b []byte) (int, error) {
+ return p.r.Read(b)
+}
+
+func (p pipe) Write(b []byte) (int, error) {
+ return p.w.Write(b)
+}
+
+func (p pipe) Close() error {
+ p.r.Close()
+ p.w.Close()
+ return nil
+}
+
+type logIO struct {
+ t *testing.T
+ prefix string
+ proxy io.ReadWriteCloser
+}
+
+func (log *logIO) Read(p []byte) (n int, err error) {
+ log.t.Logf("%s reading %d\n", log.prefix, len(p))
+ n, err = log.proxy.Read(p)
+ if err != nil {
+ log.t.Logf("%s read %x: %v\n", log.prefix, p[0:n], err)
+ } else {
+ log.t.Logf("%s read:\n%s\n", log.prefix, hex.Dump(p[0:n]))
+ //fmt.Printf("%s read:\n%s\n", log.prefix, hex.Dump(p[0:n]))
+ }
+ return
+}
+
+func (log *logIO) Write(p []byte) (n int, err error) {
+ log.t.Logf("%s writing %d\n", log.prefix, len(p))
+ n, err = log.proxy.Write(p)
+ if err != nil {
+ log.t.Logf("%s write %d, %x: %v\n", log.prefix, len(p), p[0:n], err)
+ } else {
+ log.t.Logf("%s write %d:\n%s", log.prefix, len(p), hex.Dump(p[0:n]))
+ //fmt.Printf("%s write %d:\n%s", log.prefix, len(p), hex.Dump(p[0:n]))
+ }
+ return
+}
+
+func (log *logIO) Close() (err error) {
+ err = log.proxy.Close()
+ if err != nil {
+ log.t.Logf("%s close : %v\n", log.prefix, err)
+ } else {
+ log.t.Logf("%s close\n", log.prefix)
+ }
+ return
+}
diff --git a/vendor/github.com/streadway/amqp/spec/amqp0-9-1.stripped.extended.xml b/vendor/github.com/streadway/amqp/spec/amqp0-9-1.stripped.extended.xml
new file mode 100644
index 00000000..fbddb93a
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/spec/amqp0-9-1.stripped.extended.xml
@@ -0,0 +1,537 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Errata: Section 1.2 ought to define an exception 312 "No route", which used to
+ exist in 0-9 and is what RabbitMQ sends back with 'basic.return' when a
+ 'mandatory' message cannot be delivered to any queue.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vendor/github.com/streadway/amqp/spec/gen.go b/vendor/github.com/streadway/amqp/spec/gen.go
new file mode 100644
index 00000000..69147696
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/spec/gen.go
@@ -0,0 +1,536 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+// +build ignore
+
+package main
+
+import (
+ "bytes"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "regexp"
+ "strings"
+ "text/template"
+)
+
+var (
+ ErrUnknownType = errors.New("Unknown field type in gen")
+ ErrUnknownDomain = errors.New("Unknown domain type in gen")
+)
+
+var amqpTypeToNative = map[string]string{
+ "bit": "bool",
+ "octet": "byte",
+ "shortshort": "uint8",
+ "short": "uint16",
+ "long": "uint32",
+ "longlong": "uint64",
+ "timestamp": "time.Time",
+ "table": "Table",
+ "shortstr": "string",
+ "longstr": "string",
+}
+
+type Rule struct {
+ Name string `xml:"name,attr"`
+ Docs []string `xml:"doc"`
+}
+
+type Doc struct {
+ Type string `xml:"type,attr"`
+ Body string `xml:",innerxml"`
+}
+
+type Chassis struct {
+ Name string `xml:"name,attr"`
+ Implement string `xml:"implement,attr"`
+}
+
+type Assert struct {
+ Check string `xml:"check,attr"`
+ Value string `xml:"value,attr"`
+ Method string `xml:"method,attr"`
+}
+
+type Field struct {
+ Name string `xml:"name,attr"`
+ Domain string `xml:"domain,attr"`
+ Type string `xml:"type,attr"`
+ Label string `xml:"label,attr"`
+ Reserved bool `xml:"reserved,attr"`
+ Docs []Doc `xml:"doc"`
+ Asserts []Assert `xml:"assert"`
+}
+
+type Response struct {
+ Name string `xml:"name,attr"`
+}
+
+type Method struct {
+ Name string `xml:"name,attr"`
+ Response Response `xml:"response"`
+ Synchronous bool `xml:"synchronous,attr"`
+ Content bool `xml:"content,attr"`
+ Index string `xml:"index,attr"`
+ Label string `xml:"label,attr"`
+ Docs []Doc `xml:"doc"`
+ Rules []Rule `xml:"rule"`
+ Fields []Field `xml:"field"`
+ Chassis []Chassis `xml:"chassis"`
+}
+
+type Class struct {
+ Name string `xml:"name,attr"`
+ Handler string `xml:"handler,attr"`
+ Index string `xml:"index,attr"`
+ Label string `xml:"label,attr"`
+ Docs []Doc `xml:"doc"`
+ Methods []Method `xml:"method"`
+ Chassis []Chassis `xml:"chassis"`
+}
+
+type Domain struct {
+ Name string `xml:"name,attr"`
+ Type string `xml:"type,attr"`
+ Label string `xml:"label,attr"`
+ Rules []Rule `xml:"rule"`
+ Docs []Doc `xml:"doc"`
+}
+
+type Constant struct {
+ Name string `xml:"name,attr"`
+ Value int `xml:"value,attr"`
+ Class string `xml:"class,attr"`
+ Doc string `xml:"doc"`
+}
+
+type Amqp struct {
+ Major int `xml:"major,attr"`
+ Minor int `xml:"minor,attr"`
+ Port int `xml:"port,attr"`
+ Comment string `xml:"comment,attr"`
+
+ Constants []Constant `xml:"constant"`
+ Domains []Domain `xml:"domain"`
+ Classes []Class `xml:"class"`
+}
+
+type renderer struct {
+ Root Amqp
+ bitcounter int
+}
+
+type fieldset struct {
+ AmqpType string
+ NativeType string
+ Fields []Field
+ *renderer
+}
+
+var (
+ helpers = template.FuncMap{
+ "public": public,
+ "private": private,
+ "clean": clean,
+ }
+
+ packageTemplate = template.Must(template.New("package").Funcs(helpers).Parse(`
+ // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+ // Use of this source code is governed by a BSD-style
+ // license that can be found in the LICENSE file.
+ // Source code and contact info at http://github.com/streadway/amqp
+
+ /* GENERATED FILE - DO NOT EDIT */
+ /* Rebuild from the spec/gen.go tool */
+
+ {{with .Root}}
+ package amqp
+
+ import (
+ "fmt"
+ "encoding/binary"
+ "io"
+ )
+
+ // Error codes that can be sent from the server during a connection or
+ // channel exception or used by the client to indicate a class of error like
+ // ErrCredentials. The text of the error is likely more interesting than
+ // these constants.
+ const (
+ {{range $c := .Constants}}
+ {{if $c.IsError}}{{.Name | public}}{{else}}{{.Name | private}}{{end}} = {{.Value}}{{end}}
+ )
+
+ func isSoftExceptionCode(code int) bool {
+ switch code {
+ {{range $c := .Constants}} {{if $c.IsSoftError}} case {{$c.Value}}:
+ return true
+ {{end}}{{end}}
+ }
+ return false
+ }
+
+ {{range .Classes}}
+ {{$class := .}}
+ {{range .Methods}}
+ {{$method := .}}
+ {{$struct := $.StructName $class.Name $method.Name}}
+ {{if .Docs}}/* {{range .Docs}} {{.Body | clean}} {{end}} */{{end}}
+ type {{$struct}} struct {
+ {{range .Fields}}
+ {{$.FieldName .}} {{$.FieldType . | $.NativeType}} {{if .Label}}// {{.Label}}{{end}}{{end}}
+ {{if .Content}}Properties properties
+ Body []byte{{end}}
+ }
+
+ func (msg *{{$struct}}) id() (uint16, uint16) {
+ return {{$class.Index}}, {{$method.Index}}
+ }
+
+ func (msg *{{$struct}}) wait() (bool) {
+ return {{.Synchronous}}{{if $.HasField "NoWait" .}} && !msg.NoWait{{end}}
+ }
+
+ {{if .Content}}
+ func (msg *{{$struct}}) getContent() (properties, []byte) {
+ return msg.Properties, msg.Body
+ }
+
+ func (msg *{{$struct}}) setContent(props properties, body []byte) {
+ msg.Properties, msg.Body = props, body
+ }
+ {{end}}
+ func (msg *{{$struct}}) write(w io.Writer) (err error) {
+ {{if $.HasType "bit" $method}}var bits byte{{end}}
+ {{.Fields | $.Fieldsets | $.Partial "enc-"}}
+ return
+ }
+
+ func (msg *{{$struct}}) read(r io.Reader) (err error) {
+ {{if $.HasType "bit" $method}}var bits byte{{end}}
+ {{.Fields | $.Fieldsets | $.Partial "dec-"}}
+ return
+ }
+ {{end}}
+ {{end}}
+
+ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err error) {
+ mf := &methodFrame {
+ ChannelId: channel,
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &mf.ClassId); err != nil {
+ return
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &mf.MethodId); err != nil {
+ return
+ }
+
+ switch mf.ClassId {
+ {{range .Classes}}
+ {{$class := .}}
+ case {{.Index}}: // {{.Name}}
+ switch mf.MethodId {
+ {{range .Methods}}
+ case {{.Index}}: // {{$class.Name}} {{.Name}}
+ //fmt.Println("NextMethod: class:{{$class.Index}} method:{{.Index}}")
+ method := &{{$.StructName $class.Name .Name}}{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+ {{end}}
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+ {{end}}
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown class %d", mf.ClassId)
+ }
+
+ return mf, nil
+ }
+ {{end}}
+
+ {{define "enc-bit"}}
+ {{range $off, $field := .Fields}}
+ if msg.{{$field | $.FieldName}} { bits |= 1 << {{$off}} }
+ {{end}}
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil { return }
+ {{end}}
+ {{define "enc-octet"}}
+ {{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "enc-shortshort"}}
+ {{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "enc-short"}}
+ {{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "enc-long"}}
+ {{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "enc-longlong"}}
+ {{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "enc-timestamp"}}
+ {{range .Fields}} if err = writeTimestamp(w, msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "enc-shortstr"}}
+ {{range .Fields}} if err = writeShortstr(w, msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "enc-longstr"}}
+ {{range .Fields}} if err = writeLongstr(w, msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "enc-table"}}
+ {{range .Fields}} if err = writeTable(w, msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+
+ {{define "dec-bit"}}
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ {{range $off, $field := .Fields}} msg.{{$field | $.FieldName}} = (bits & (1 << {{$off}}) > 0)
+ {{end}}
+ {{end}}
+ {{define "dec-octet"}}
+ {{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "dec-shortshort"}}
+ {{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "dec-short"}}
+ {{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "dec-long"}}
+ {{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "dec-longlong"}}
+ {{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "dec-timestamp"}}
+ {{range .Fields}} if msg.{{. | $.FieldName}}, err = readTimestamp(r); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "dec-shortstr"}}
+ {{range .Fields}} if msg.{{. | $.FieldName}}, err = readShortstr(r); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "dec-longstr"}}
+ {{range .Fields}} if msg.{{. | $.FieldName}}, err = readLongstr(r); err != nil { return }
+ {{end}}
+ {{end}}
+ {{define "dec-table"}}
+ {{range .Fields}} if msg.{{. | $.FieldName}}, err = readTable(r); err != nil { return }
+ {{end}}
+ {{end}}
+
+ `))
+)
+
+func (c *Constant) IsError() bool {
+ return strings.Contains(c.Class, "error")
+}
+
+func (c *Constant) IsSoftError() bool {
+ return c.Class == "soft-error"
+}
+
+func (renderer *renderer) Partial(prefix string, fields []fieldset) (s string, err error) {
+ var buf bytes.Buffer
+ for _, set := range fields {
+ name := prefix + set.AmqpType
+ t := packageTemplate.Lookup(name)
+ if t == nil {
+ return "", errors.New(fmt.Sprintf("Missing template: %s", name))
+ }
+ if err = t.Execute(&buf, set); err != nil {
+ return
+ }
+ }
+ return string(buf.Bytes()), nil
+}
+
+// Groups the fields so that the right encoder/decoder can be called
+func (renderer *renderer) Fieldsets(fields []Field) (f []fieldset, err error) {
+ if len(fields) > 0 {
+ for _, field := range fields {
+ cur := fieldset{}
+ cur.AmqpType, err = renderer.FieldType(field)
+ if err != nil {
+ return
+ }
+
+ cur.NativeType, err = renderer.NativeType(cur.AmqpType)
+ if err != nil {
+ return
+ }
+ cur.Fields = append(cur.Fields, field)
+ f = append(f, cur)
+ }
+
+ i, j := 0, 1
+ for j < len(f) {
+ if f[i].AmqpType == f[j].AmqpType {
+ f[i].Fields = append(f[i].Fields, f[j].Fields...)
+ } else {
+ i++
+ f[i] = f[j]
+ }
+ j++
+ }
+ return f[:i+1], nil
+ }
+
+ return
+}
+
+func (renderer *renderer) HasType(typ string, method Method) bool {
+ for _, f := range method.Fields {
+ name, _ := renderer.FieldType(f)
+ if name == typ {
+ return true
+ }
+ }
+ return false
+}
+
+func (renderer *renderer) HasField(field string, method Method) bool {
+ for _, f := range method.Fields {
+ name := renderer.FieldName(f)
+ if name == field {
+ return true
+ }
+ }
+ return false
+}
+
+func (renderer *renderer) Domain(field Field) (domain Domain, err error) {
+ for _, domain = range renderer.Root.Domains {
+ if field.Domain == domain.Name {
+ return
+ }
+ }
+ return domain, nil
+ //return domain, ErrUnknownDomain
+}
+
+func (renderer *renderer) FieldName(field Field) (t string) {
+ t = public(field.Name)
+
+ if field.Reserved {
+ t = strings.ToLower(t)
+ }
+
+ return
+}
+
+func (renderer *renderer) FieldType(field Field) (t string, err error) {
+ t = field.Type
+
+ if t == "" {
+ var domain Domain
+ domain, err = renderer.Domain(field)
+ if err != nil {
+ return "", err
+ }
+ t = domain.Type
+ }
+
+ return
+}
+
+func (renderer *renderer) NativeType(amqpType string) (t string, err error) {
+ if t, ok := amqpTypeToNative[amqpType]; ok {
+ return t, nil
+ }
+ return "", ErrUnknownType
+}
+
+func (renderer *renderer) Tag(d Domain) string {
+ label := "`"
+
+ label += `domain:"` + d.Name + `"`
+
+ if len(d.Type) > 0 {
+ label += `,type:"` + d.Type + `"`
+ }
+
+ label += "`"
+
+ return label
+}
+
+func (renderer *renderer) StructName(parts ...string) string {
+ return parts[0] + public(parts[1:]...)
+}
+
+func clean(body string) (res string) {
+ return strings.Replace(body, "\r", "", -1)
+}
+
+func private(parts ...string) string {
+ return export(regexp.MustCompile(`[-_]\w`), parts...)
+}
+
+func public(parts ...string) string {
+ return export(regexp.MustCompile(`^\w|[-_]\w`), parts...)
+}
+
+func export(delim *regexp.Regexp, parts ...string) (res string) {
+ for _, in := range parts {
+
+ res += delim.ReplaceAllStringFunc(in, func(match string) string {
+ switch len(match) {
+ case 1:
+ return strings.ToUpper(match)
+ case 2:
+ return strings.ToUpper(match[1:])
+ }
+ panic("unreachable")
+ })
+ }
+
+ return
+}
+
+func main() {
+ var r renderer
+
+ spec, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ log.Fatalln("Please pass spec on stdin", err)
+ }
+
+ err = xml.Unmarshal(spec, &r.Root)
+
+ if err != nil {
+ log.Fatalln("Could not parse XML:", err)
+ }
+
+ if err = packageTemplate.Execute(os.Stdout, &r); err != nil {
+ log.Fatalln("Generate error: ", err)
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/spec091.go b/vendor/github.com/streadway/amqp/spec091.go
new file mode 100644
index 00000000..cd53ebe7
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/spec091.go
@@ -0,0 +1,3306 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+/* GENERATED FILE - DO NOT EDIT */
+/* Rebuild from the spec/gen.go tool */
+
+package amqp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+// Error codes that can be sent from the server during a connection or
+// channel exception or used by the client to indicate a class of error like
+// ErrCredentials. The text of the error is likely more interesting than
+// these constants.
+const (
+ frameMethod = 1
+ frameHeader = 2
+ frameBody = 3
+ frameHeartbeat = 8
+ frameMinSize = 4096
+ frameEnd = 206
+ replySuccess = 200
+ ContentTooLarge = 311
+ NoRoute = 312
+ NoConsumers = 313
+ ConnectionForced = 320
+ InvalidPath = 402
+ AccessRefused = 403
+ NotFound = 404
+ ResourceLocked = 405
+ PreconditionFailed = 406
+ FrameError = 501
+ SyntaxError = 502
+ CommandInvalid = 503
+ ChannelError = 504
+ UnexpectedFrame = 505
+ ResourceError = 506
+ NotAllowed = 530
+ NotImplemented = 540
+ InternalError = 541
+)
+
+func isSoftExceptionCode(code int) bool {
+ switch code {
+ case 311:
+ return true
+ case 312:
+ return true
+ case 313:
+ return true
+ case 403:
+ return true
+ case 404:
+ return true
+ case 405:
+ return true
+ case 406:
+ return true
+
+ }
+ return false
+}
+
+type connectionStart struct {
+ VersionMajor byte
+ VersionMinor byte
+ ServerProperties Table
+ Mechanisms string
+ Locales string
+}
+
+func (msg *connectionStart) id() (uint16, uint16) {
+ return 10, 10
+}
+
+func (msg *connectionStart) wait() bool {
+ return true
+}
+
+func (msg *connectionStart) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.VersionMajor); err != nil {
+ return
+ }
+ if err = binary.Write(w, binary.BigEndian, msg.VersionMinor); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.ServerProperties); err != nil {
+ return
+ }
+
+ if err = writeLongstr(w, msg.Mechanisms); err != nil {
+ return
+ }
+ if err = writeLongstr(w, msg.Locales); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionStart) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.VersionMajor); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &msg.VersionMinor); err != nil {
+ return
+ }
+
+ if msg.ServerProperties, err = readTable(r); err != nil {
+ return
+ }
+
+ if msg.Mechanisms, err = readLongstr(r); err != nil {
+ return
+ }
+ if msg.Locales, err = readLongstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionStartOk struct {
+ ClientProperties Table
+ Mechanism string
+ Response string
+ Locale string
+}
+
+func (msg *connectionStartOk) id() (uint16, uint16) {
+ return 10, 11
+}
+
+func (msg *connectionStartOk) wait() bool {
+ return true
+}
+
+func (msg *connectionStartOk) write(w io.Writer) (err error) {
+
+ if err = writeTable(w, msg.ClientProperties); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Mechanism); err != nil {
+ return
+ }
+
+ if err = writeLongstr(w, msg.Response); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Locale); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionStartOk) read(r io.Reader) (err error) {
+
+ if msg.ClientProperties, err = readTable(r); err != nil {
+ return
+ }
+
+ if msg.Mechanism, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if msg.Response, err = readLongstr(r); err != nil {
+ return
+ }
+
+ if msg.Locale, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionSecure struct {
+ Challenge string
+}
+
+func (msg *connectionSecure) id() (uint16, uint16) {
+ return 10, 20
+}
+
+func (msg *connectionSecure) wait() bool {
+ return true
+}
+
+func (msg *connectionSecure) write(w io.Writer) (err error) {
+
+ if err = writeLongstr(w, msg.Challenge); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionSecure) read(r io.Reader) (err error) {
+
+ if msg.Challenge, err = readLongstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionSecureOk struct {
+ Response string
+}
+
+func (msg *connectionSecureOk) id() (uint16, uint16) {
+ return 10, 21
+}
+
+func (msg *connectionSecureOk) wait() bool {
+ return true
+}
+
+func (msg *connectionSecureOk) write(w io.Writer) (err error) {
+
+ if err = writeLongstr(w, msg.Response); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionSecureOk) read(r io.Reader) (err error) {
+
+ if msg.Response, err = readLongstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionTune struct {
+ ChannelMax uint16
+ FrameMax uint32
+ Heartbeat uint16
+}
+
+func (msg *connectionTune) id() (uint16, uint16) {
+ return 10, 30
+}
+
+func (msg *connectionTune) wait() bool {
+ return true
+}
+
+func (msg *connectionTune) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ChannelMax); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.FrameMax); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.Heartbeat); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionTune) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ChannelMax); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.FrameMax); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.Heartbeat); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionTuneOk struct {
+ ChannelMax uint16
+ FrameMax uint32
+ Heartbeat uint16
+}
+
+func (msg *connectionTuneOk) id() (uint16, uint16) {
+ return 10, 31
+}
+
+func (msg *connectionTuneOk) wait() bool {
+ return true
+}
+
+func (msg *connectionTuneOk) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ChannelMax); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.FrameMax); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.Heartbeat); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionTuneOk) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ChannelMax); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.FrameMax); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.Heartbeat); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionOpen struct {
+ VirtualHost string
+ reserved1 string
+ reserved2 bool
+}
+
+func (msg *connectionOpen) id() (uint16, uint16) {
+ return 10, 40
+}
+
+func (msg *connectionOpen) wait() bool {
+ return true
+}
+
+func (msg *connectionOpen) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = writeShortstr(w, msg.VirtualHost); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.reserved2 {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionOpen) read(r io.Reader) (err error) {
+ var bits byte
+
+ if msg.VirtualHost, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.reserved1, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.reserved2 = (bits&(1<<0) > 0)
+
+ return
+}
+
+type connectionOpenOk struct {
+ reserved1 string
+}
+
+func (msg *connectionOpenOk) id() (uint16, uint16) {
+ return 10, 41
+}
+
+func (msg *connectionOpenOk) wait() bool {
+ return true
+}
+
+func (msg *connectionOpenOk) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionOpenOk) read(r io.Reader) (err error) {
+
+ if msg.reserved1, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionClose struct {
+ ReplyCode uint16
+ ReplyText string
+ ClassId uint16
+ MethodId uint16
+}
+
+func (msg *connectionClose) id() (uint16, uint16) {
+ return 10, 50
+}
+
+func (msg *connectionClose) wait() bool {
+ return true
+}
+
+func (msg *connectionClose) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ReplyCode); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.ReplyText); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.ClassId); err != nil {
+ return
+ }
+ if err = binary.Write(w, binary.BigEndian, msg.MethodId); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionClose) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ReplyCode); err != nil {
+ return
+ }
+
+ if msg.ReplyText, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ClassId); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &msg.MethodId); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionCloseOk struct {
+}
+
+func (msg *connectionCloseOk) id() (uint16, uint16) {
+ return 10, 51
+}
+
+func (msg *connectionCloseOk) wait() bool {
+ return true
+}
+
+func (msg *connectionCloseOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *connectionCloseOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type connectionBlocked struct {
+ Reason string
+}
+
+func (msg *connectionBlocked) id() (uint16, uint16) {
+ return 10, 60
+}
+
+func (msg *connectionBlocked) wait() bool {
+ return false
+}
+
+func (msg *connectionBlocked) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.Reason); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionBlocked) read(r io.Reader) (err error) {
+
+ if msg.Reason, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionUnblocked struct {
+}
+
+func (msg *connectionUnblocked) id() (uint16, uint16) {
+ return 10, 61
+}
+
+func (msg *connectionUnblocked) wait() bool {
+ return false
+}
+
+func (msg *connectionUnblocked) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *connectionUnblocked) read(r io.Reader) (err error) {
+
+ return
+}
+
+type channelOpen struct {
+ reserved1 string
+}
+
+func (msg *channelOpen) id() (uint16, uint16) {
+ return 20, 10
+}
+
+func (msg *channelOpen) wait() bool {
+ return true
+}
+
+func (msg *channelOpen) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelOpen) read(r io.Reader) (err error) {
+
+ if msg.reserved1, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type channelOpenOk struct {
+ reserved1 string
+}
+
+func (msg *channelOpenOk) id() (uint16, uint16) {
+ return 20, 11
+}
+
+func (msg *channelOpenOk) wait() bool {
+ return true
+}
+
+func (msg *channelOpenOk) write(w io.Writer) (err error) {
+
+ if err = writeLongstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelOpenOk) read(r io.Reader) (err error) {
+
+ if msg.reserved1, err = readLongstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type channelFlow struct {
+ Active bool
+}
+
+func (msg *channelFlow) id() (uint16, uint16) {
+ return 20, 20
+}
+
+func (msg *channelFlow) wait() bool {
+ return true
+}
+
+func (msg *channelFlow) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Active {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelFlow) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Active = (bits&(1<<0) > 0)
+
+ return
+}
+
+type channelFlowOk struct {
+ Active bool
+}
+
+func (msg *channelFlowOk) id() (uint16, uint16) {
+ return 20, 21
+}
+
+func (msg *channelFlowOk) wait() bool {
+ return false
+}
+
+func (msg *channelFlowOk) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Active {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelFlowOk) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Active = (bits&(1<<0) > 0)
+
+ return
+}
+
+type channelClose struct {
+ ReplyCode uint16
+ ReplyText string
+ ClassId uint16
+ MethodId uint16
+}
+
+func (msg *channelClose) id() (uint16, uint16) {
+ return 20, 40
+}
+
+func (msg *channelClose) wait() bool {
+ return true
+}
+
+func (msg *channelClose) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ReplyCode); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.ReplyText); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.ClassId); err != nil {
+ return
+ }
+ if err = binary.Write(w, binary.BigEndian, msg.MethodId); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelClose) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ReplyCode); err != nil {
+ return
+ }
+
+ if msg.ReplyText, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ClassId); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &msg.MethodId); err != nil {
+ return
+ }
+
+ return
+}
+
+type channelCloseOk struct {
+}
+
+func (msg *channelCloseOk) id() (uint16, uint16) {
+ return 20, 41
+}
+
+func (msg *channelCloseOk) wait() bool {
+ return true
+}
+
+func (msg *channelCloseOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *channelCloseOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type exchangeDeclare struct {
+ reserved1 uint16
+ Exchange string
+ Type string
+ Passive bool
+ Durable bool
+ AutoDelete bool
+ Internal bool
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *exchangeDeclare) id() (uint16, uint16) {
+ return 40, 10
+}
+
+func (msg *exchangeDeclare) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *exchangeDeclare) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Type); err != nil {
+ return
+ }
+
+ if msg.Passive {
+ bits |= 1 << 0
+ }
+
+ if msg.Durable {
+ bits |= 1 << 1
+ }
+
+ if msg.AutoDelete {
+ bits |= 1 << 2
+ }
+
+ if msg.Internal {
+ bits |= 1 << 3
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 4
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *exchangeDeclare) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Type, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Passive = (bits&(1<<0) > 0)
+ msg.Durable = (bits&(1<<1) > 0)
+ msg.AutoDelete = (bits&(1<<2) > 0)
+ msg.Internal = (bits&(1<<3) > 0)
+ msg.NoWait = (bits&(1<<4) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type exchangeDeclareOk struct {
+}
+
+func (msg *exchangeDeclareOk) id() (uint16, uint16) {
+ return 40, 11
+}
+
+func (msg *exchangeDeclareOk) wait() bool {
+ return true
+}
+
+func (msg *exchangeDeclareOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *exchangeDeclareOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type exchangeDelete struct {
+ reserved1 uint16
+ Exchange string
+ IfUnused bool
+ NoWait bool
+}
+
+func (msg *exchangeDelete) id() (uint16, uint16) {
+ return 40, 20
+}
+
+func (msg *exchangeDelete) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *exchangeDelete) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+
+ if msg.IfUnused {
+ bits |= 1 << 0
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 1
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *exchangeDelete) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.IfUnused = (bits&(1<<0) > 0)
+ msg.NoWait = (bits&(1<<1) > 0)
+
+ return
+}
+
+type exchangeDeleteOk struct {
+}
+
+func (msg *exchangeDeleteOk) id() (uint16, uint16) {
+ return 40, 21
+}
+
+func (msg *exchangeDeleteOk) wait() bool {
+ return true
+}
+
+func (msg *exchangeDeleteOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *exchangeDeleteOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type exchangeBind struct {
+ reserved1 uint16
+ Destination string
+ Source string
+ RoutingKey string
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *exchangeBind) id() (uint16, uint16) {
+ return 40, 30
+}
+
+func (msg *exchangeBind) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *exchangeBind) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Destination); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Source); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *exchangeBind) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Destination, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Source, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type exchangeBindOk struct {
+}
+
+func (msg *exchangeBindOk) id() (uint16, uint16) {
+ return 40, 31
+}
+
+func (msg *exchangeBindOk) wait() bool {
+ return true
+}
+
+func (msg *exchangeBindOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *exchangeBindOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type exchangeUnbind struct {
+ reserved1 uint16
+ Destination string
+ Source string
+ RoutingKey string
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *exchangeUnbind) id() (uint16, uint16) {
+ return 40, 40
+}
+
+func (msg *exchangeUnbind) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *exchangeUnbind) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Destination); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Source); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *exchangeUnbind) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Destination, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Source, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type exchangeUnbindOk struct {
+}
+
+func (msg *exchangeUnbindOk) id() (uint16, uint16) {
+ return 40, 51
+}
+
+func (msg *exchangeUnbindOk) wait() bool {
+ return true
+}
+
+func (msg *exchangeUnbindOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *exchangeUnbindOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type queueDeclare struct {
+ reserved1 uint16
+ Queue string
+ Passive bool
+ Durable bool
+ Exclusive bool
+ AutoDelete bool
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *queueDeclare) id() (uint16, uint16) {
+ return 50, 10
+}
+
+func (msg *queueDeclare) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *queueDeclare) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if msg.Passive {
+ bits |= 1 << 0
+ }
+
+ if msg.Durable {
+ bits |= 1 << 1
+ }
+
+ if msg.Exclusive {
+ bits |= 1 << 2
+ }
+
+ if msg.AutoDelete {
+ bits |= 1 << 3
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 4
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueDeclare) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Passive = (bits&(1<<0) > 0)
+ msg.Durable = (bits&(1<<1) > 0)
+ msg.Exclusive = (bits&(1<<2) > 0)
+ msg.AutoDelete = (bits&(1<<3) > 0)
+ msg.NoWait = (bits&(1<<4) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueDeclareOk struct {
+ Queue string
+ MessageCount uint32
+ ConsumerCount uint32
+}
+
+func (msg *queueDeclareOk) id() (uint16, uint16) {
+ return 50, 11
+}
+
+func (msg *queueDeclareOk) wait() bool {
+ return true
+}
+
+func (msg *queueDeclareOk) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.MessageCount); err != nil {
+ return
+ }
+ if err = binary.Write(w, binary.BigEndian, msg.ConsumerCount); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueDeclareOk) read(r io.Reader) (err error) {
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.MessageCount); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &msg.ConsumerCount); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueBind struct {
+ reserved1 uint16
+ Queue string
+ Exchange string
+ RoutingKey string
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *queueBind) id() (uint16, uint16) {
+ return 50, 20
+}
+
+func (msg *queueBind) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *queueBind) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueBind) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueBindOk struct {
+}
+
+func (msg *queueBindOk) id() (uint16, uint16) {
+ return 50, 21
+}
+
+func (msg *queueBindOk) wait() bool {
+ return true
+}
+
+func (msg *queueBindOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *queueBindOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type queueUnbind struct {
+ reserved1 uint16
+ Queue string
+ Exchange string
+ RoutingKey string
+ Arguments Table
+}
+
+func (msg *queueUnbind) id() (uint16, uint16) {
+ return 50, 50
+}
+
+func (msg *queueUnbind) wait() bool {
+ return true
+}
+
+func (msg *queueUnbind) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueUnbind) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueUnbindOk struct {
+}
+
+func (msg *queueUnbindOk) id() (uint16, uint16) {
+ return 50, 51
+}
+
+func (msg *queueUnbindOk) wait() bool {
+ return true
+}
+
+func (msg *queueUnbindOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *queueUnbindOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type queuePurge struct {
+ reserved1 uint16
+ Queue string
+ NoWait bool
+}
+
+func (msg *queuePurge) id() (uint16, uint16) {
+ return 50, 30
+}
+
+func (msg *queuePurge) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *queuePurge) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queuePurge) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ return
+}
+
+type queuePurgeOk struct {
+ MessageCount uint32
+}
+
+func (msg *queuePurgeOk) id() (uint16, uint16) {
+ return 50, 31
+}
+
+func (msg *queuePurgeOk) wait() bool {
+ return true
+}
+
+func (msg *queuePurgeOk) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queuePurgeOk) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueDelete struct {
+ reserved1 uint16
+ Queue string
+ IfUnused bool
+ IfEmpty bool
+ NoWait bool
+}
+
+func (msg *queueDelete) id() (uint16, uint16) {
+ return 50, 40
+}
+
+func (msg *queueDelete) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *queueDelete) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if msg.IfUnused {
+ bits |= 1 << 0
+ }
+
+ if msg.IfEmpty {
+ bits |= 1 << 1
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 2
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueDelete) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.IfUnused = (bits&(1<<0) > 0)
+ msg.IfEmpty = (bits&(1<<1) > 0)
+ msg.NoWait = (bits&(1<<2) > 0)
+
+ return
+}
+
+type queueDeleteOk struct {
+ MessageCount uint32
+}
+
+func (msg *queueDeleteOk) id() (uint16, uint16) {
+ return 50, 41
+}
+
+func (msg *queueDeleteOk) wait() bool {
+ return true
+}
+
+func (msg *queueDeleteOk) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueDeleteOk) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicQos struct {
+ PrefetchSize uint32
+ PrefetchCount uint16
+ Global bool
+}
+
+func (msg *basicQos) id() (uint16, uint16) {
+ return 60, 10
+}
+
+func (msg *basicQos) wait() bool {
+ return true
+}
+
+func (msg *basicQos) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.PrefetchSize); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.PrefetchCount); err != nil {
+ return
+ }
+
+ if msg.Global {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicQos) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.PrefetchSize); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.PrefetchCount); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Global = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicQosOk struct {
+}
+
+func (msg *basicQosOk) id() (uint16, uint16) {
+ return 60, 11
+}
+
+func (msg *basicQosOk) wait() bool {
+ return true
+}
+
+func (msg *basicQosOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *basicQosOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type basicConsume struct {
+ reserved1 uint16
+ Queue string
+ ConsumerTag string
+ NoLocal bool
+ NoAck bool
+ Exclusive bool
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *basicConsume) id() (uint16, uint16) {
+ return 60, 20
+}
+
+func (msg *basicConsume) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *basicConsume) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ if msg.NoLocal {
+ bits |= 1 << 0
+ }
+
+ if msg.NoAck {
+ bits |= 1 << 1
+ }
+
+ if msg.Exclusive {
+ bits |= 1 << 2
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 3
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicConsume) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoLocal = (bits&(1<<0) > 0)
+ msg.NoAck = (bits&(1<<1) > 0)
+ msg.Exclusive = (bits&(1<<2) > 0)
+ msg.NoWait = (bits&(1<<3) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicConsumeOk struct {
+ ConsumerTag string
+}
+
+func (msg *basicConsumeOk) id() (uint16, uint16) {
+ return 60, 21
+}
+
+func (msg *basicConsumeOk) wait() bool {
+ return true
+}
+
+func (msg *basicConsumeOk) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicConsumeOk) read(r io.Reader) (err error) {
+
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicCancel struct {
+ ConsumerTag string
+ NoWait bool
+}
+
+func (msg *basicCancel) id() (uint16, uint16) {
+ return 60, 30
+}
+
+func (msg *basicCancel) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *basicCancel) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicCancel) read(r io.Reader) (err error) {
+ var bits byte
+
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicCancelOk struct {
+ ConsumerTag string
+}
+
+func (msg *basicCancelOk) id() (uint16, uint16) {
+ return 60, 31
+}
+
+func (msg *basicCancelOk) wait() bool {
+ return true
+}
+
+func (msg *basicCancelOk) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicCancelOk) read(r io.Reader) (err error) {
+
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicPublish struct {
+ reserved1 uint16
+ Exchange string
+ RoutingKey string
+ Mandatory bool
+ Immediate bool
+ Properties properties
+ Body []byte
+}
+
+func (msg *basicPublish) id() (uint16, uint16) {
+ return 60, 40
+}
+
+func (msg *basicPublish) wait() bool {
+ return false
+}
+
+func (msg *basicPublish) getContent() (properties, []byte) {
+ return msg.Properties, msg.Body
+}
+
+func (msg *basicPublish) setContent(props properties, body []byte) {
+ msg.Properties, msg.Body = props, body
+}
+
+func (msg *basicPublish) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if msg.Mandatory {
+ bits |= 1 << 0
+ }
+
+ if msg.Immediate {
+ bits |= 1 << 1
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicPublish) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Mandatory = (bits&(1<<0) > 0)
+ msg.Immediate = (bits&(1<<1) > 0)
+
+ return
+}
+
+type basicReturn struct {
+ ReplyCode uint16
+ ReplyText string
+ Exchange string
+ RoutingKey string
+ Properties properties
+ Body []byte
+}
+
+func (msg *basicReturn) id() (uint16, uint16) {
+ return 60, 50
+}
+
+func (msg *basicReturn) wait() bool {
+ return false
+}
+
+func (msg *basicReturn) getContent() (properties, []byte) {
+ return msg.Properties, msg.Body
+}
+
+func (msg *basicReturn) setContent(props properties, body []byte) {
+ msg.Properties, msg.Body = props, body
+}
+
+func (msg *basicReturn) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ReplyCode); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.ReplyText); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicReturn) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ReplyCode); err != nil {
+ return
+ }
+
+ if msg.ReplyText, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicDeliver struct {
+ ConsumerTag string
+ DeliveryTag uint64
+ Redelivered bool
+ Exchange string
+ RoutingKey string
+ Properties properties
+ Body []byte
+}
+
+func (msg *basicDeliver) id() (uint16, uint16) {
+ return 60, 60
+}
+
+func (msg *basicDeliver) wait() bool {
+ return false
+}
+
+func (msg *basicDeliver) getContent() (properties, []byte) {
+ return msg.Properties, msg.Body
+}
+
+func (msg *basicDeliver) setContent(props properties, body []byte) {
+ msg.Properties, msg.Body = props, body
+}
+
+func (msg *basicDeliver) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Redelivered {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicDeliver) read(r io.Reader) (err error) {
+ var bits byte
+
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Redelivered = (bits&(1<<0) > 0)
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicGet struct {
+ reserved1 uint16
+ Queue string
+ NoAck bool
+}
+
+func (msg *basicGet) id() (uint16, uint16) {
+ return 60, 70
+}
+
+func (msg *basicGet) wait() bool {
+ return true
+}
+
+func (msg *basicGet) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if msg.NoAck {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicGet) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoAck = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicGetOk struct {
+ DeliveryTag uint64
+ Redelivered bool
+ Exchange string
+ RoutingKey string
+ MessageCount uint32
+ Properties properties
+ Body []byte
+}
+
+func (msg *basicGetOk) id() (uint16, uint16) {
+ return 60, 71
+}
+
+func (msg *basicGetOk) wait() bool {
+ return true
+}
+
+func (msg *basicGetOk) getContent() (properties, []byte) {
+ return msg.Properties, msg.Body
+}
+
+func (msg *basicGetOk) setContent(props properties, body []byte) {
+ msg.Properties, msg.Body = props, body
+}
+
+func (msg *basicGetOk) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Redelivered {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicGetOk) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Redelivered = (bits&(1<<0) > 0)
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicGetEmpty struct {
+ reserved1 string
+}
+
+func (msg *basicGetEmpty) id() (uint16, uint16) {
+ return 60, 72
+}
+
+func (msg *basicGetEmpty) wait() bool {
+ return true
+}
+
+func (msg *basicGetEmpty) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicGetEmpty) read(r io.Reader) (err error) {
+
+ if msg.reserved1, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicAck struct {
+ DeliveryTag uint64
+ Multiple bool
+}
+
+func (msg *basicAck) id() (uint16, uint16) {
+ return 60, 80
+}
+
+func (msg *basicAck) wait() bool {
+ return false
+}
+
+func (msg *basicAck) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Multiple {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicAck) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Multiple = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicReject struct {
+ DeliveryTag uint64
+ Requeue bool
+}
+
+func (msg *basicReject) id() (uint16, uint16) {
+ return 60, 90
+}
+
+func (msg *basicReject) wait() bool {
+ return false
+}
+
+func (msg *basicReject) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Requeue {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicReject) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Requeue = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicRecoverAsync struct {
+ Requeue bool
+}
+
+func (msg *basicRecoverAsync) id() (uint16, uint16) {
+ return 60, 100
+}
+
+func (msg *basicRecoverAsync) wait() bool {
+ return false
+}
+
+func (msg *basicRecoverAsync) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Requeue {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicRecoverAsync) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Requeue = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicRecover struct {
+ Requeue bool
+}
+
+func (msg *basicRecover) id() (uint16, uint16) {
+ return 60, 110
+}
+
+func (msg *basicRecover) wait() bool {
+ return true
+}
+
+func (msg *basicRecover) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Requeue {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicRecover) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Requeue = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicRecoverOk struct {
+}
+
+func (msg *basicRecoverOk) id() (uint16, uint16) {
+ return 60, 111
+}
+
+func (msg *basicRecoverOk) wait() bool {
+ return true
+}
+
+func (msg *basicRecoverOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *basicRecoverOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type basicNack struct {
+ DeliveryTag uint64
+ Multiple bool
+ Requeue bool
+}
+
+func (msg *basicNack) id() (uint16, uint16) {
+ return 60, 120
+}
+
+func (msg *basicNack) wait() bool {
+ return false
+}
+
+func (msg *basicNack) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Multiple {
+ bits |= 1 << 0
+ }
+
+ if msg.Requeue {
+ bits |= 1 << 1
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicNack) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Multiple = (bits&(1<<0) > 0)
+ msg.Requeue = (bits&(1<<1) > 0)
+
+ return
+}
+
+type txSelect struct {
+}
+
+func (msg *txSelect) id() (uint16, uint16) {
+ return 90, 10
+}
+
+func (msg *txSelect) wait() bool {
+ return true
+}
+
+func (msg *txSelect) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txSelect) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txSelectOk struct {
+}
+
+func (msg *txSelectOk) id() (uint16, uint16) {
+ return 90, 11
+}
+
+func (msg *txSelectOk) wait() bool {
+ return true
+}
+
+func (msg *txSelectOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txSelectOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txCommit struct {
+}
+
+func (msg *txCommit) id() (uint16, uint16) {
+ return 90, 20
+}
+
+func (msg *txCommit) wait() bool {
+ return true
+}
+
+func (msg *txCommit) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txCommit) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txCommitOk struct {
+}
+
+func (msg *txCommitOk) id() (uint16, uint16) {
+ return 90, 21
+}
+
+func (msg *txCommitOk) wait() bool {
+ return true
+}
+
+func (msg *txCommitOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txCommitOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txRollback struct {
+}
+
+func (msg *txRollback) id() (uint16, uint16) {
+ return 90, 30
+}
+
+func (msg *txRollback) wait() bool {
+ return true
+}
+
+func (msg *txRollback) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txRollback) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txRollbackOk struct {
+}
+
+func (msg *txRollbackOk) id() (uint16, uint16) {
+ return 90, 31
+}
+
+func (msg *txRollbackOk) wait() bool {
+ return true
+}
+
+func (msg *txRollbackOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txRollbackOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type confirmSelect struct {
+ Nowait bool
+}
+
+func (msg *confirmSelect) id() (uint16, uint16) {
+ return 85, 10
+}
+
+func (msg *confirmSelect) wait() bool {
+ return true
+}
+
+func (msg *confirmSelect) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Nowait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *confirmSelect) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Nowait = (bits&(1<<0) > 0)
+
+ return
+}
+
+type confirmSelectOk struct {
+}
+
+func (msg *confirmSelectOk) id() (uint16, uint16) {
+ return 85, 11
+}
+
+func (msg *confirmSelectOk) wait() bool {
+ return true
+}
+
+func (msg *confirmSelectOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *confirmSelectOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err error) {
+ mf := &methodFrame{
+ ChannelId: channel,
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &mf.ClassId); err != nil {
+ return
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &mf.MethodId); err != nil {
+ return
+ }
+
+ switch mf.ClassId {
+
+ case 10: // connection
+ switch mf.MethodId {
+
+ case 10: // connection start
+ //fmt.Println("NextMethod: class:10 method:10")
+ method := &connectionStart{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // connection start-ok
+ //fmt.Println("NextMethod: class:10 method:11")
+ method := &connectionStartOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // connection secure
+ //fmt.Println("NextMethod: class:10 method:20")
+ method := &connectionSecure{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // connection secure-ok
+ //fmt.Println("NextMethod: class:10 method:21")
+ method := &connectionSecureOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // connection tune
+ //fmt.Println("NextMethod: class:10 method:30")
+ method := &connectionTune{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // connection tune-ok
+ //fmt.Println("NextMethod: class:10 method:31")
+ method := &connectionTuneOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // connection open
+ //fmt.Println("NextMethod: class:10 method:40")
+ method := &connectionOpen{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 41: // connection open-ok
+ //fmt.Println("NextMethod: class:10 method:41")
+ method := &connectionOpenOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 50: // connection close
+ //fmt.Println("NextMethod: class:10 method:50")
+ method := &connectionClose{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 51: // connection close-ok
+ //fmt.Println("NextMethod: class:10 method:51")
+ method := &connectionCloseOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 60: // connection blocked
+ //fmt.Println("NextMethod: class:10 method:60")
+ method := &connectionBlocked{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 61: // connection unblocked
+ //fmt.Println("NextMethod: class:10 method:61")
+ method := &connectionUnblocked{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 20: // channel
+ switch mf.MethodId {
+
+ case 10: // channel open
+ //fmt.Println("NextMethod: class:20 method:10")
+ method := &channelOpen{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // channel open-ok
+ //fmt.Println("NextMethod: class:20 method:11")
+ method := &channelOpenOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // channel flow
+ //fmt.Println("NextMethod: class:20 method:20")
+ method := &channelFlow{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // channel flow-ok
+ //fmt.Println("NextMethod: class:20 method:21")
+ method := &channelFlowOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // channel close
+ //fmt.Println("NextMethod: class:20 method:40")
+ method := &channelClose{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 41: // channel close-ok
+ //fmt.Println("NextMethod: class:20 method:41")
+ method := &channelCloseOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 40: // exchange
+ switch mf.MethodId {
+
+ case 10: // exchange declare
+ //fmt.Println("NextMethod: class:40 method:10")
+ method := &exchangeDeclare{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // exchange declare-ok
+ //fmt.Println("NextMethod: class:40 method:11")
+ method := &exchangeDeclareOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // exchange delete
+ //fmt.Println("NextMethod: class:40 method:20")
+ method := &exchangeDelete{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // exchange delete-ok
+ //fmt.Println("NextMethod: class:40 method:21")
+ method := &exchangeDeleteOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // exchange bind
+ //fmt.Println("NextMethod: class:40 method:30")
+ method := &exchangeBind{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // exchange bind-ok
+ //fmt.Println("NextMethod: class:40 method:31")
+ method := &exchangeBindOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // exchange unbind
+ //fmt.Println("NextMethod: class:40 method:40")
+ method := &exchangeUnbind{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 51: // exchange unbind-ok
+ //fmt.Println("NextMethod: class:40 method:51")
+ method := &exchangeUnbindOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 50: // queue
+ switch mf.MethodId {
+
+ case 10: // queue declare
+ //fmt.Println("NextMethod: class:50 method:10")
+ method := &queueDeclare{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // queue declare-ok
+ //fmt.Println("NextMethod: class:50 method:11")
+ method := &queueDeclareOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // queue bind
+ //fmt.Println("NextMethod: class:50 method:20")
+ method := &queueBind{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // queue bind-ok
+ //fmt.Println("NextMethod: class:50 method:21")
+ method := &queueBindOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 50: // queue unbind
+ //fmt.Println("NextMethod: class:50 method:50")
+ method := &queueUnbind{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 51: // queue unbind-ok
+ //fmt.Println("NextMethod: class:50 method:51")
+ method := &queueUnbindOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // queue purge
+ //fmt.Println("NextMethod: class:50 method:30")
+ method := &queuePurge{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // queue purge-ok
+ //fmt.Println("NextMethod: class:50 method:31")
+ method := &queuePurgeOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // queue delete
+ //fmt.Println("NextMethod: class:50 method:40")
+ method := &queueDelete{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 41: // queue delete-ok
+ //fmt.Println("NextMethod: class:50 method:41")
+ method := &queueDeleteOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 60: // basic
+ switch mf.MethodId {
+
+ case 10: // basic qos
+ //fmt.Println("NextMethod: class:60 method:10")
+ method := &basicQos{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // basic qos-ok
+ //fmt.Println("NextMethod: class:60 method:11")
+ method := &basicQosOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // basic consume
+ //fmt.Println("NextMethod: class:60 method:20")
+ method := &basicConsume{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // basic consume-ok
+ //fmt.Println("NextMethod: class:60 method:21")
+ method := &basicConsumeOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // basic cancel
+ //fmt.Println("NextMethod: class:60 method:30")
+ method := &basicCancel{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // basic cancel-ok
+ //fmt.Println("NextMethod: class:60 method:31")
+ method := &basicCancelOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // basic publish
+ //fmt.Println("NextMethod: class:60 method:40")
+ method := &basicPublish{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 50: // basic return
+ //fmt.Println("NextMethod: class:60 method:50")
+ method := &basicReturn{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 60: // basic deliver
+ //fmt.Println("NextMethod: class:60 method:60")
+ method := &basicDeliver{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 70: // basic get
+ //fmt.Println("NextMethod: class:60 method:70")
+ method := &basicGet{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 71: // basic get-ok
+ //fmt.Println("NextMethod: class:60 method:71")
+ method := &basicGetOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 72: // basic get-empty
+ //fmt.Println("NextMethod: class:60 method:72")
+ method := &basicGetEmpty{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 80: // basic ack
+ //fmt.Println("NextMethod: class:60 method:80")
+ method := &basicAck{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 90: // basic reject
+ //fmt.Println("NextMethod: class:60 method:90")
+ method := &basicReject{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 100: // basic recover-async
+ //fmt.Println("NextMethod: class:60 method:100")
+ method := &basicRecoverAsync{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 110: // basic recover
+ //fmt.Println("NextMethod: class:60 method:110")
+ method := &basicRecover{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 111: // basic recover-ok
+ //fmt.Println("NextMethod: class:60 method:111")
+ method := &basicRecoverOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 120: // basic nack
+ //fmt.Println("NextMethod: class:60 method:120")
+ method := &basicNack{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 90: // tx
+ switch mf.MethodId {
+
+ case 10: // tx select
+ //fmt.Println("NextMethod: class:90 method:10")
+ method := &txSelect{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // tx select-ok
+ //fmt.Println("NextMethod: class:90 method:11")
+ method := &txSelectOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // tx commit
+ //fmt.Println("NextMethod: class:90 method:20")
+ method := &txCommit{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // tx commit-ok
+ //fmt.Println("NextMethod: class:90 method:21")
+ method := &txCommitOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // tx rollback
+ //fmt.Println("NextMethod: class:90 method:30")
+ method := &txRollback{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // tx rollback-ok
+ //fmt.Println("NextMethod: class:90 method:31")
+ method := &txRollbackOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 85: // confirm
+ switch mf.MethodId {
+
+ case 10: // confirm select
+ //fmt.Println("NextMethod: class:85 method:10")
+ method := &confirmSelect{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // confirm select-ok
+ //fmt.Println("NextMethod: class:85 method:11")
+ method := &confirmSelectOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown class %d", mf.ClassId)
+ }
+
+ return mf, nil
+}
diff --git a/vendor/github.com/streadway/amqp/tls_test.go b/vendor/github.com/streadway/amqp/tls_test.go
new file mode 100644
index 00000000..f6021c96
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/tls_test.go
@@ -0,0 +1,223 @@
+package amqp_test
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io"
+ "net"
+ "testing"
+ "time"
+
+ "github.com/streadway/amqp"
+)
+
+type tlsServer struct {
+ net.Listener
+ URL string
+ Config *tls.Config
+ Header chan []byte
+}
+
+// Captures the header for each accepted connection
+func (s *tlsServer) Serve() {
+ for {
+ c, err := s.Accept()
+ if err != nil {
+ return
+ }
+
+ header := make([]byte, 4)
+ io.ReadFull(c, header)
+ s.Header <- header
+ c.Write([]byte{'A', 'M', 'Q', 'P', 0, 0, 0, 0})
+ c.Close()
+ }
+}
+
+func tlsConfig() *tls.Config {
+ cfg := new(tls.Config)
+
+ cfg.ClientCAs = x509.NewCertPool()
+ cfg.ClientCAs.AppendCertsFromPEM([]byte(caCert))
+
+ cert, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
+ if err != nil {
+ panic(err)
+ }
+
+ cfg.Certificates = append(cfg.Certificates, cert)
+ cfg.ClientAuth = tls.RequireAndVerifyClientCert
+
+ return cfg
+}
+
+func startTlsServer() tlsServer {
+ cfg := tlsConfig()
+
+ l, err := tls.Listen("tcp", "127.0.0.1:0", cfg)
+ if err != nil {
+ panic(err)
+ }
+
+ s := tlsServer{
+ Listener: l,
+ Config: cfg,
+ URL: fmt.Sprintf("amqps://%s/", l.Addr().String()),
+ Header: make(chan []byte, 1),
+ }
+
+ go s.Serve()
+ return s
+}
+
+// Tests that the server has handshaked the connection and seen the client
+// protocol announcement. Does not nest that the connection.open is successful.
+func TestTLSHandshake(t *testing.T) {
+ srv := startTlsServer()
+ defer srv.Close()
+
+ cfg := new(tls.Config)
+ cfg.RootCAs = x509.NewCertPool()
+ cfg.RootCAs.AppendCertsFromPEM([]byte(caCert))
+
+ cert, _ := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
+ cfg.Certificates = append(cfg.Certificates, cert)
+
+ c, err := amqp.DialTLS(srv.URL, cfg)
+
+ select {
+ case <-time.After(10 * time.Millisecond):
+ t.Fatalf("did not succeed to handshake the TLS connection after 10ms")
+ case header := <-srv.Header:
+ if string(header) != "AMQP" {
+ t.Fatalf("expected to handshake a TLS connection, got err: %v", err)
+ }
+ }
+
+ if st := c.ConnectionState(); !st.HandshakeComplete {
+ t.Errorf("TLS handshake failed, TLS connection state: %+v", st)
+ }
+}
+
+const caCert = `
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa6gAwIBAgIJANWuMWMQSxvdMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV
+BAMTCE15VGVzdENBMB4XDTE0MDEyNzE5NTIyMloXDTI0MDEyNTE5NTIyMlowEzER
+MA8GA1UEAxMITXlUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDBsIrkW4ob9Z/gzR2/Maa2stbutry6/vvz8eiJwIKIbaHGwqtFOUGiWeKw7H76
+IH3SjTAhNQY2hoKPyH41D36sDJkYBRyHFJTK/6ffvOhpyLnuXJAnoS62eKPSNUAx
+5i/lkHj42ESutYAH9qbHCI/gBm9G4WmhGAyA16xzC1n07JObl6KFoY1PqHKl823z
+mvF47I24DzemEfjdwC9nAAX/pGYOg9FA9nQv7NnhlsJMxueCx55RNU1ADRoqsbfE
+T0CQTOT4ryugGrUp9J4Cwen6YbXZrS6+Kff5SQCAns0Qu8/bwj0DKkuBGLF+Mnwe
+mq9bMzyZPUrPM3Gu48ao8YAfAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0P
+BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCBwXGblRxIEOlEP6ANZ1C8AHWyG8lR
+CQduFclc0tmyCCz5fnyLK0aGu9LhXXe6/HSKqgs4mJqeqYOojdjkfOme/YdwDzjK
+WIf0kRYQHcB6NeyEZwW8C7subTP1Xw6zbAmjvQrtCGvRM+fi3/cs1sSSkd/EoRk4
+7GM9qQl/JIIoCOGncninf2NQm5YSpbit6/mOQD7EhqXsw+bX+IRh3DHC1Apv/PoA
+HlDNeM4vjWaBxsmvRSndrIvew1czboFM18oRSSIqAkU7dKZ0SbC11grzmNxMG2aD
+f9y8FIG6RK/SEaOZuc+uBGXx7tj7dczpE/2puqYcaVGwcv4kkrC/ZuRm
+-----END CERTIFICATE-----
+`
+
+const serverCert = `
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdugAwIBAgIBATANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl
+c3RDQTAeFw0xNDAxMjcxOTUyMjNaFw0yNDAxMjUxOTUyMjNaMCUxEjAQBgNVBAMT
+CTEyNy4wLjAuMTEPMA0GA1UEChMGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAxYAKbeGyg0gP0xwVsZsufzk/SUCtD44Gp3lQYQ9QumQ1IVZu
+PmZWwPWrzI93a1Abruz6ZhXaB3jcL5QPAy1N44IiFgVN45CZXBsqkpJe/abzRFOV
+DRnHxattPDHdgwML5d3nURKGUM/7+ACj5E4pZEDlM3RIjIKVd+doJsL7n6myO8FE
+tIpt4vTz1MFp3F+ntPnHU3BZ/VZ1UjSlFWnCjT0CR0tnXsPmlIaC98HThS8x5zNB
+fvvSN+Zln8RWdNLnEVHVdqYtOQ828QbCx8s1HfClGgaVoSDrzz+qQgtZFO4wW264
+2CWkNd8DSJUJ/HlPNXmbXsrRMgvGaL7YUz2yRQIDAQABo0AwPjAJBgNVHRMEAjAA
+MAsGA1UdDwQEAwIFIDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHREECDAGhwR/
+AAABMA0GCSqGSIb3DQEBBQUAA4IBAQAE2g+wAFf9Xg5svcnb7+mfseYV16k9l5WG
+onrmR3FLsbTxfbr4PZJMHrswPbi2NRk0+ETPUpcv1RP7pUB7wSEvuS1NPGcU92iP
+58ycP3dYtLzmuu6BkgToZqwsCU8fC2zM0wt3+ifzPpDMffWWOioVuA3zdM9WPQYz
++Ofajd0XaZwFZS8uTI5WXgObz7Xqfmln4tF3Sq1CTyuJ44qK4p83XOKFq+L04aD0
+d0c8w3YQNUENny/vMP9mDu3FQ3SnDz2GKl1LSjGe2TUnkoMkDfdk4wSzndTz/ecb
+QiCPKijwVPWNOWV3NDE2edMxDPxDoKoEm5F4UGfGjxSRnYCIoZLh
+-----END CERTIFICATE-----
+`
+
+const serverKey = `
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxYAKbeGyg0gP0xwVsZsufzk/SUCtD44Gp3lQYQ9QumQ1IVZu
+PmZWwPWrzI93a1Abruz6ZhXaB3jcL5QPAy1N44IiFgVN45CZXBsqkpJe/abzRFOV
+DRnHxattPDHdgwML5d3nURKGUM/7+ACj5E4pZEDlM3RIjIKVd+doJsL7n6myO8FE
+tIpt4vTz1MFp3F+ntPnHU3BZ/VZ1UjSlFWnCjT0CR0tnXsPmlIaC98HThS8x5zNB
+fvvSN+Zln8RWdNLnEVHVdqYtOQ828QbCx8s1HfClGgaVoSDrzz+qQgtZFO4wW264
+2CWkNd8DSJUJ/HlPNXmbXsrRMgvGaL7YUz2yRQIDAQABAoIBAGsyEvcPAGg3DbfE
+z5WFp9gPx2TIAOanbL8rnlAAEw4H47qDgfTGcSHsdeHioKuTYGMyZrpP8/YISGJe
+l0NfLJ5mfH+9Q0hXrJWMfS/u2DYOjo0wXH8u1fpZEEISwqsgVS3fonSjfFmSea1j
+E5GQRvEONBkYbWQuYFgjNqmLPS2r5lKbWCQvc1MB/vvVBwOTiO0ON7m/EkM5RKt9
+cDT5ZhhVjBpdmd9HpVbKTdBj8Q0l5/ZHZUEgZA6FDZEwYxTd9l87Z4YT+5SR0z9t
+k8/Z0CHd3x3Rv891t7m66ZJkaOda8NC65/432MQEQwJltmrKnc22dS8yI26rrmpp
+g3tcbSUCgYEA5nMXdQKS4vF+Kp10l/HqvGz2sU8qQaWYZQIg7Th3QJPo6N52po/s
+nn3UF0P5mT1laeZ5ZQJKx4gnmuPnIZ2ZtJQDyFhIbRPcZ+2hSNSuLYVcrumOC3EP
+3OZyFtFE1THO73aFe5e1jEdtoOne3Bds/Hq6NF45fkVdL+M9e8pfXIsCgYEA22W8
+zGjbWyrFOYvKknMQVtHnMx8BJEtsvWRknP6CWAv/8WyeZpE128Pve1m441AQnopS
+CuOF5wFK0iUXBFbS3Pe1/1j3em6yfVznuUHqJ7Qc+dNzxVvkTK8jGB6x+vm+M9Hg
+muHUM726IUxckoSNXbPNAVPIZab1NdSxam7F9m8CgYEAx55QZmIJXJ41XLKxqWC7
+peZ5NpPNlbncrTpPzUzJN94ntXfmrVckbxGt401VayEctMQYyZ9XqUlOjUP3FU5Q
+M3S3Zhba/eljVX8o406fZf0MkNLs4QpZ5E6V6x/xEP+pMhKng6yhbVb+JpIPIvUD
+yhyBKRWplbB+DRo5Sv685gsCgYA7l5m9h+m1DJv/cnn2Z2yTuHXtC8namuYRV1iA
+0ByFX9UINXGc+GpBpCnDPm6ax5+MAJQiQwSW52H0TIDA+/hQbrQvhHHL/o9av8Zt
+Kns4h5KrRQUYIUqUjamhnozHV9iS6LnyN87Usv8AlmY6oehoADN53dD702qdUYVT
+HH2G3wKBgCdvqyw78FR/n8cUWesTPnxx5HCeWJ1J+2BESnUnPmKZ71CV1H7uweja
+vPUxuuuGLKfNx84OKCfRDbtOgMOeyh9T1RmXry6Srz/7/udjlF0qmFiRXfBNAgoR
+tNb0+Ri/vY0AHrQ7UnCbl12qPVaqhEXLr+kCGNEPFqpMJPPEeMK0
+-----END RSA PRIVATE KEY-----
+`
+
+const clientCert = `
+-----BEGIN CERTIFICATE-----
+MIIC4jCCAcqgAwIBAgIBAjANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl
+c3RDQTAeFw0xNDAxMjcxOTUyMjNaFw0yNDAxMjUxOTUyMjNaMCUxEjAQBgNVBAMT
+CTEyNy4wLjAuMTEPMA0GA1UEChMGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAu7LMqd+agoH168Bsi0WJ36ulYqDypq+GZPF7uWOo2pE0raKH
+B++31/hjnkt6yC5kLKVZZ0EfolBa9q4Cy6swfGaEMafy44ZCRneLnt1azL1N6Kfz
++U0KsOqyQDoMxYJG1gVTEZN19/U/ew2eazcxKyERI3oGCQ4SbpkxBTbfxtAFk49e
+xIB3obsuMVUrmtXE4FkUkvG7NgpPUgrhp0yxYpj9zruZGzGGT1zNhcarbQ/4i7It
+ZMbnv6pqQWtYDgnGX2TDRcEiXGeO+KrzhfpTRLfO3K4np8e8cmTyXM+4lMlWUgma
+KrRdu1QXozGqRs47u2prGKGdSQWITpqNVCY8fQIDAQABoy8wLTAJBgNVHRMEAjAA
+MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQUF
+AAOCAQEAhCuBCLznPc4O96hT3P8Fx19L3ltrWbc/pWrx8JjxUaGk8kNmjMjY+/Mt
+JBbjUBx2kJwaY0EHMAfw7D1f1wcCeNycx/0dyb0E6xzhmPw5fY15GGNg8rzWwqSY
++i/1iqU0IRkmRHV7XCF+trd2H0Ec+V1Fd/61E2ccJfOL5aSAyWbMCUtWxS3QMnqH
+FBfKdVEiY9WNht5hnvsXQBRaNhowJ6Cwa7/1/LZjmhcXiJ0xrc1Hggj3cvS+4vll
+Ew+20a0tPKjD/v/2oSQL+qkeYKV4fhCGkaBHCpPlSJrqorb7B6NmPy3nS26ETKE/
+o2UCfZc5g2MU1ENa31kT1iuhKZapsA==
+-----END CERTIFICATE-----
+`
+
+const clientKey = `
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAu7LMqd+agoH168Bsi0WJ36ulYqDypq+GZPF7uWOo2pE0raKH
+B++31/hjnkt6yC5kLKVZZ0EfolBa9q4Cy6swfGaEMafy44ZCRneLnt1azL1N6Kfz
++U0KsOqyQDoMxYJG1gVTEZN19/U/ew2eazcxKyERI3oGCQ4SbpkxBTbfxtAFk49e
+xIB3obsuMVUrmtXE4FkUkvG7NgpPUgrhp0yxYpj9zruZGzGGT1zNhcarbQ/4i7It
+ZMbnv6pqQWtYDgnGX2TDRcEiXGeO+KrzhfpTRLfO3K4np8e8cmTyXM+4lMlWUgma
+KrRdu1QXozGqRs47u2prGKGdSQWITpqNVCY8fQIDAQABAoIBAGSEn3hFyEAmCyYi
+2b5IEksXaC2GlgxQKb/7Vs/0oCPU6YonZPsKFMFzQx4tu+ZiecEzF8rlJGTPdbdv
+fw3FcuTcHeVd1QSmDO4h7UK5tnu40XVMJKsY6CXQun8M13QajYbmORNLjjypOULU
+C0fNueYoAj6mhX7p61MRdSAev/5+0+bVQQG/tSVDQzdngvKpaCunOphiB2VW2Aa0
+7aYPOFCoPB2uo0DwUmBB0yfx9x4hXX9ovQI0YFou7bq6iYJ0vlZBvYQ9YrVdxjKL
+avcz1N5xM3WFAkZJSVT/Ho5+uTbZx4RrJ8b5T+t2spOKmXyAjwS2rL/XMAh8YRZ1
+u44duoECgYEA4jpK2qshgQ0t49rjVHEDKX5x7ElEZefl0rHZ/2X/uHUDKpKj2fTq
+3TQzHquiQ4Aof7OEB9UE3DGrtpvo/j/PYxL5Luu5VR4AIEJm+CA8GYuE96+uIL0Z
+M2r3Lux6Bp30Z47Eit2KiY4fhrWs59WB3NHHoFxgzHSVbnuA02gcX2ECgYEA1GZw
+iXIVYaK07ED+q/0ObyS5hD1cMhJ7ifSN9BxuG0qUpSigbkTGj09fUDS4Fqsz9dvz
+F0P93fZvyia242TIfDUwJEsDQCgHk7SGa4Rx/p/3x/obIEERk7K76Hdg93U5NXhV
+NvczvgL0HYxnb+qtumwMgGPzncB4lGcTnRyOfp0CgYBTIsDnYwRI/KLknUf1fCKB
+WSpcfwBXwsS+jQVjygQTsUyclI8KResZp1kx6DkVPT+kzj+y8SF8GfTUgq844BJC
+gnJ4P8A3+3JoaH6WqKHtcUxICZOgDF36e1CjOdwOGnX6qIipz4hdzJDhXFpSSDAV
+CjKmR8x61k0j8NcC2buzgQKBgFr7eo9VwBTvpoJhIPY5UvqHB7S+uAR26FZi3H/J
+wdyM6PmKWpaBfXCb9l8cBhMnyP0y94FqzY9L5fz48nSbkkmqWvHg9AaCXySFOuNJ
+e68vhOszlnUNimLzOAzPPkkh/JyL7Cy8XXyyNTGHGDPXmg12BTDmH8/eR4iCUuOE
+/QD9AoGBALQ/SkvfO3D5+k9e/aTHRuMJ0+PWdLUMTZ39oJQxUx+qj7/xpjDvWTBn
+eDmF/wjnIAg+020oXyBYo6plEZfDz3EYJQZ+3kLLEU+O/A7VxCakPYPwCr7N/InL
+Ccg/TVSIXxw/6uJnojoAjMIEU45NoP6RMp0mWYYb2OlteEv08Ovp
+-----END RSA PRIVATE KEY-----
+`
diff --git a/vendor/github.com/streadway/amqp/types.go b/vendor/github.com/streadway/amqp/types.go
new file mode 100644
index 00000000..ff5ea3cb
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/types.go
@@ -0,0 +1,427 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "fmt"
+ "io"
+ "time"
+)
+
+// Constants for standard AMQP 0-9-1 exchange types.
+const (
+ ExchangeDirect = "direct"
+ ExchangeFanout = "fanout"
+ ExchangeTopic = "topic"
+ ExchangeHeaders = "headers"
+)
+
+var (
+ // ErrClosed is returned when the channel or connection is not open
+ ErrClosed = &Error{Code: ChannelError, Reason: "channel/connection is not open"}
+
+ // ErrChannelMax is returned when Connection.Channel has been called enough
+ // times that all channel IDs have been exhausted in the client or the
+ // server.
+ ErrChannelMax = &Error{Code: ChannelError, Reason: "channel id space exhausted"}
+
+ // ErrSASL is returned from Dial when the authentication mechanism could not
+ // be negoated.
+ ErrSASL = &Error{Code: AccessRefused, Reason: "SASL could not negotiate a shared mechanism"}
+
+ // ErrCredentials is returned when the authenticated client is not authorized
+ // to any vhost.
+ ErrCredentials = &Error{Code: AccessRefused, Reason: "username or password not allowed"}
+
+ // ErrVhost is returned when the authenticated user is not permitted to
+ // access the requested Vhost.
+ ErrVhost = &Error{Code: AccessRefused, Reason: "no access to this vhost"}
+
+ // ErrSyntax is hard protocol error, indicating an unsupported protocol,
+ // implementation or encoding.
+ ErrSyntax = &Error{Code: SyntaxError, Reason: "invalid field or value inside of a frame"}
+
+ // ErrFrame is returned when the protocol frame cannot be read from the
+ // server, indicating an unsupported protocol or unsupported frame type.
+ ErrFrame = &Error{Code: FrameError, Reason: "frame could not be parsed"}
+
+ // ErrCommandInvalid is returned when the server sends an unexpected response
+ // to this requested message type. This indicates a bug in this client.
+ ErrCommandInvalid = &Error{Code: CommandInvalid, Reason: "unexpected command received"}
+
+ // ErrUnexpectedFrame is returned when something other than a method or
+ // heartbeat frame is delivered to the Connection, indicating a bug in the
+ // client.
+ ErrUnexpectedFrame = &Error{Code: UnexpectedFrame, Reason: "unexpected frame received"}
+
+ // ErrFieldType is returned when writing a message containing a Go type unsupported by AMQP.
+ ErrFieldType = &Error{Code: SyntaxError, Reason: "unsupported table field type"}
+)
+
+// Error captures the code and reason a channel or connection has been closed
+// by the server.
+type Error struct {
+ Code int // constant code from the specification
+ Reason string // description of the error
+ Server bool // true when initiated from the server, false when from this library
+ Recover bool // true when this error can be recovered by retrying later or with different parameters
+}
+
+func newError(code uint16, text string) *Error {
+ return &Error{
+ Code: int(code),
+ Reason: text,
+ Recover: isSoftExceptionCode(int(code)),
+ Server: true,
+ }
+}
+
+func (e Error) Error() string {
+ return fmt.Sprintf("Exception (%d) Reason: %q", e.Code, e.Reason)
+}
+
+// Used by header frames to capture routing and header information
+type properties struct {
+ ContentType string // MIME content type
+ ContentEncoding string // MIME content encoding
+ Headers Table // Application or header exchange table
+ DeliveryMode uint8 // queue implementation use - Transient (1) or Persistent (2)
+ Priority uint8 // queue implementation use - 0 to 9
+ CorrelationId string // application use - correlation identifier
+ ReplyTo string // application use - address to to reply to (ex: RPC)
+ Expiration string // implementation use - message expiration spec
+ MessageId string // application use - message identifier
+ Timestamp time.Time // application use - message timestamp
+ Type string // application use - message type name
+ UserId string // application use - creating user id
+ AppId string // application use - creating application
+ reserved1 string // was cluster-id - process for buffer consumption
+}
+
+// DeliveryMode. Transient means higher throughput but messages will not be
+// restored on broker restart. The delivery mode of publishings is unrelated
+// to the durability of the queues they reside on. Transient messages will
+// not be restored to durable queues, persistent messages will be restored to
+// durable queues and lost on non-durable queues during server restart.
+//
+// This remains typed as uint8 to match Publishing.DeliveryMode. Other
+// delivery modes specific to custom queue implementations are not enumerated
+// here.
+const (
+ Transient uint8 = 1
+ Persistent uint8 = 2
+)
+
+// The property flags are an array of bits that indicate the presence or
+// absence of each property value in sequence. The bits are ordered from most
+// high to low - bit 15 indicates the first property.
+const (
+ flagContentType = 0x8000
+ flagContentEncoding = 0x4000
+ flagHeaders = 0x2000
+ flagDeliveryMode = 0x1000
+ flagPriority = 0x0800
+ flagCorrelationId = 0x0400
+ flagReplyTo = 0x0200
+ flagExpiration = 0x0100
+ flagMessageId = 0x0080
+ flagTimestamp = 0x0040
+ flagType = 0x0020
+ flagUserId = 0x0010
+ flagAppId = 0x0008
+ flagReserved1 = 0x0004
+)
+
+// Queue captures the current server state of the queue on the server returned
+// from Channel.QueueDeclare or Channel.QueueInspect.
+type Queue struct {
+ Name string // server confirmed or generated name
+ Messages int // count of messages not awaiting acknowledgment
+ Consumers int // number of consumers receiving deliveries
+}
+
+// Publishing captures the client message sent to the server. The fields
+// outside of the Headers table included in this struct mirror the underlying
+// fields in the content frame. They use native types for convenience and
+// efficiency.
+type Publishing struct {
+ // Application or exchange specific fields,
+ // the headers exchange will inspect this field.
+ Headers Table
+
+ // Properties
+ ContentType string // MIME content type
+ ContentEncoding string // MIME content encoding
+ DeliveryMode uint8 // Transient (0 or 1) or Persistent (2)
+ Priority uint8 // 0 to 9
+ CorrelationId string // correlation identifier
+ ReplyTo string // address to to reply to (ex: RPC)
+ Expiration string // message expiration spec
+ MessageId string // message identifier
+ Timestamp time.Time // message timestamp
+ Type string // message type name
+ UserId string // creating user id - ex: "guest"
+ AppId string // creating application id
+
+ // The application specific payload of the message
+ Body []byte
+}
+
+// Blocking notifies the server's TCP flow control of the Connection. When a
+// server hits a memory or disk alarm it will block all connections until the
+// resources are reclaimed. Use NotifyBlock on the Connection to receive these
+// events.
+type Blocking struct {
+ Active bool // TCP pushback active/inactive on server
+ Reason string // Server reason for activation
+}
+
+// Confirmation notifies the acknowledgment or negative acknowledgement of a
+// publishing identified by its delivery tag. Use NotifyPublish on the Channel
+// to consume these events.
+type Confirmation struct {
+ DeliveryTag uint64 // A 1 based counter of publishings from when the channel was put in Confirm mode
+ Ack bool // True when the server successfully received the publishing
+}
+
+// Decimal matches the AMQP decimal type. Scale is the number of decimal
+// digits Scale == 2, Value == 12345, Decimal == 123.45
+type Decimal struct {
+ Scale uint8
+ Value int32
+}
+
+// Table stores user supplied fields of the following types:
+//
+// bool
+// byte
+// float32
+// float64
+// int16
+// int32
+// int64
+// nil
+// string
+// time.Time
+// amqp.Decimal
+// amqp.Table
+// []byte
+// []interface{} - containing above types
+//
+// Functions taking a table will immediately fail when the table contains a
+// value of an unsupported type.
+//
+// The caller must be specific in which precision of integer it wishes to
+// encode.
+//
+// Use a type assertion when reading values from a table for type conversion.
+//
+// RabbitMQ expects int32 for integer values.
+//
+type Table map[string]interface{}
+
+func validateField(f interface{}) error {
+ switch fv := f.(type) {
+ case nil, bool, byte, int16, int32, int64, float32, float64, string, []byte, Decimal, time.Time:
+ return nil
+
+ case []interface{}:
+ for _, v := range fv {
+ if err := validateField(v); err != nil {
+ return fmt.Errorf("in array %s", err)
+ }
+ }
+ return nil
+
+ case Table:
+ for k, v := range fv {
+ if err := validateField(v); err != nil {
+ return fmt.Errorf("table field %q %s", k, err)
+ }
+ }
+ return nil
+ }
+
+ return fmt.Errorf("value %t not supported", f)
+}
+
+// Validate returns and error if any Go types in the table are incompatible with AMQP types.
+func (t Table) Validate() error {
+ return validateField(t)
+}
+
+// Heap interface for maintaining delivery tags
+type tagSet []uint64
+
+func (set tagSet) Len() int { return len(set) }
+func (set tagSet) Less(i, j int) bool { return (set)[i] < (set)[j] }
+func (set tagSet) Swap(i, j int) { (set)[i], (set)[j] = (set)[j], (set)[i] }
+func (set *tagSet) Push(tag interface{}) { *set = append(*set, tag.(uint64)) }
+func (set *tagSet) Pop() interface{} {
+ val := (*set)[len(*set)-1]
+ *set = (*set)[:len(*set)-1]
+ return val
+}
+
+type message interface {
+ id() (uint16, uint16)
+ wait() bool
+ read(io.Reader) error
+ write(io.Writer) error
+}
+
+type messageWithContent interface {
+ message
+ getContent() (properties, []byte)
+ setContent(properties, []byte)
+}
+
+/*
+The base interface implemented as:
+
+2.3.5 frame Details
+
+All frames consist of a header (7 octets), a payload of arbitrary size, and a 'frame-end' octet that detects
+malformed frames:
+
+ 0 1 3 7 size+7 size+8
+ +------+---------+-------------+ +------------+ +-----------+
+ | type | channel | size | | payload | | frame-end |
+ +------+---------+-------------+ +------------+ +-----------+
+ octet short long size octets octet
+
+To read a frame, we:
+
+ 1. Read the header and check the frame type and channel.
+ 2. Depending on the frame type, we read the payload and process it.
+ 3. Read the frame end octet.
+
+In realistic implementations where performance is a concern, we would use
+“read-ahead buffering” or “gathering reads” to avoid doing three separate
+system calls to read a frame.
+
+*/
+type frame interface {
+ write(io.Writer) error
+ channel() uint16
+}
+
+type reader struct {
+ r io.Reader
+}
+
+type writer struct {
+ w io.Writer
+}
+
+// Implements the frame interface for Connection RPC
+type protocolHeader struct{}
+
+func (protocolHeader) write(w io.Writer) error {
+ _, err := w.Write([]byte{'A', 'M', 'Q', 'P', 0, 0, 9, 1})
+ return err
+}
+
+func (protocolHeader) channel() uint16 {
+ panic("only valid as initial handshake")
+}
+
+/*
+Method frames carry the high-level protocol commands (which we call "methods").
+One method frame carries one command. The method frame payload has this format:
+
+ 0 2 4
+ +----------+-----------+-------------- - -
+ | class-id | method-id | arguments...
+ +----------+-----------+-------------- - -
+ short short ...
+
+To process a method frame, we:
+ 1. Read the method frame payload.
+ 2. Unpack it into a structure. A given method always has the same structure,
+ so we can unpack the method rapidly. 3. Check that the method is allowed in
+ the current context.
+ 4. Check that the method arguments are valid.
+ 5. Execute the method.
+
+Method frame bodies are constructed as a list of AMQP data fields (bits,
+integers, strings and string tables). The marshalling code is trivially
+generated directly from the protocol specifications, and can be very rapid.
+*/
+type methodFrame struct {
+ ChannelId uint16
+ ClassId uint16
+ MethodId uint16
+ Method message
+}
+
+func (f *methodFrame) channel() uint16 { return f.ChannelId }
+
+/*
+Heartbeating is a technique designed to undo one of TCP/IP's features, namely
+its ability to recover from a broken physical connection by closing only after
+a quite long time-out. In some scenarios we need to know very rapidly if a
+peer is disconnected or not responding for other reasons (e.g. it is looping).
+Since heartbeating can be done at a low level, we implement this as a special
+type of frame that peers exchange at the transport level, rather than as a
+class method.
+*/
+type heartbeatFrame struct {
+ ChannelId uint16
+}
+
+func (f *heartbeatFrame) channel() uint16 { return f.ChannelId }
+
+/*
+Certain methods (such as Basic.Publish, Basic.Deliver, etc.) are formally
+defined as carrying content. When a peer sends such a method frame, it always
+follows it with a content header and zero or more content body frames.
+
+A content header frame has this format:
+
+ 0 2 4 12 14
+ +----------+--------+-----------+----------------+------------- - -
+ | class-id | weight | body size | property flags | property list...
+ +----------+--------+-----------+----------------+------------- - -
+ short short long long short remainder...
+
+We place content body in distinct frames (rather than including it in the
+method) so that AMQP may support "zero copy" techniques in which content is
+never marshalled or encoded. We place the content properties in their own
+frame so that recipients can selectively discard contents they do not want to
+process
+*/
+type headerFrame struct {
+ ChannelId uint16
+ ClassId uint16
+ weight uint16
+ Size uint64
+ Properties properties
+}
+
+func (f *headerFrame) channel() uint16 { return f.ChannelId }
+
+/*
+Content is the application data we carry from client-to-client via the AMQP
+server. Content is, roughly speaking, a set of properties plus a binary data
+part. The set of allowed properties are defined by the Basic class, and these
+form the "content header frame". The data can be any size, and MAY be broken
+into several (or many) chunks, each forming a "content body frame".
+
+Looking at the frames for a specific channel, as they pass on the wire, we
+might see something like this:
+
+ [method]
+ [method] [header] [body] [body]
+ [method]
+ ...
+*/
+type bodyFrame struct {
+ ChannelId uint16
+ Body []byte
+}
+
+func (f *bodyFrame) channel() uint16 { return f.ChannelId }
diff --git a/vendor/github.com/streadway/amqp/uri.go b/vendor/github.com/streadway/amqp/uri.go
new file mode 100644
index 00000000..ee006b32
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/uri.go
@@ -0,0 +1,175 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "errors"
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+)
+
+var errURIScheme = errors.New("AMQP scheme must be either 'amqp://' or 'amqps://'")
+var errURIWhitespace = errors.New("URI must not contain whitespace")
+
+var schemePorts = map[string]int{
+ "amqp": 5672,
+ "amqps": 5671,
+}
+
+var defaultURI = URI{
+ Scheme: "amqp",
+ Host: "localhost",
+ Port: 5672,
+ Username: "guest",
+ Password: "guest",
+ Vhost: "/",
+}
+
+// URI represents a parsed AMQP URI string.
+type URI struct {
+ Scheme string
+ Host string
+ Port int
+ Username string
+ Password string
+ Vhost string
+}
+
+// ParseURI attempts to parse the given AMQP URI according to the spec.
+// See http://www.rabbitmq.com/uri-spec.html.
+//
+// Default values for the fields are:
+//
+// Scheme: amqp
+// Host: localhost
+// Port: 5672
+// Username: guest
+// Password: guest
+// Vhost: /
+//
+func ParseURI(uri string) (URI, error) {
+ builder := defaultURI
+
+ if strings.Contains(uri, " ") == true {
+ return builder, errURIWhitespace
+ }
+
+ u, err := url.Parse(uri)
+ if err != nil {
+ return builder, err
+ }
+
+ defaultPort, okScheme := schemePorts[u.Scheme]
+
+ if okScheme {
+ builder.Scheme = u.Scheme
+ } else {
+ return builder, errURIScheme
+ }
+
+ host, port := splitHostPort(u.Host)
+
+ if host != "" {
+ builder.Host = host
+ }
+
+ if port != "" {
+ port32, err := strconv.ParseInt(port, 10, 32)
+ if err != nil {
+ return builder, err
+ }
+ builder.Port = int(port32)
+ } else {
+ builder.Port = defaultPort
+ }
+
+ if u.User != nil {
+ builder.Username = u.User.Username()
+ if password, ok := u.User.Password(); ok {
+ builder.Password = password
+ }
+ }
+
+ if u.Path != "" {
+ if strings.HasPrefix(u.Path, "/") {
+ if u.Host == "" && strings.HasPrefix(u.Path, "///") {
+ // net/url doesn't handle local context authorities and leaves that up
+ // to the scheme handler. In our case, we translate amqp:/// into the
+ // default host and whatever the vhost should be
+ if len(u.Path) > 3 {
+ builder.Vhost = u.Path[3:]
+ }
+ } else if len(u.Path) > 1 {
+ builder.Vhost = u.Path[1:]
+ }
+ } else {
+ builder.Vhost = u.Path
+ }
+ }
+
+ return builder, nil
+}
+
+// Splits host:port, host, [ho:st]:port, or [ho:st]. Unlike net.SplitHostPort
+// which splits :port, host:port or [host]:port
+//
+// Handles hosts that have colons that are in brackets like [::1]:http
+func splitHostPort(addr string) (host, port string) {
+ i := strings.LastIndex(addr, ":")
+
+ if i >= 0 {
+ host, port = addr[:i], addr[i+1:]
+
+ if len(port) > 0 && port[len(port)-1] == ']' && addr[0] == '[' {
+ // we've split on an inner colon, the port was missing outside of the
+ // brackets so use the full addr. We could assert that host should not
+ // contain any colons here
+ host, port = addr, ""
+ }
+ } else {
+ host = addr
+ }
+
+ return
+}
+
+// PlainAuth returns a PlainAuth structure based on the parsed URI's
+// Username and Password fields.
+func (uri URI) PlainAuth() *PlainAuth {
+ return &PlainAuth{
+ Username: uri.Username,
+ Password: uri.Password,
+ }
+}
+
+func (uri URI) String() string {
+ var authority string
+
+ if uri.Username != defaultURI.Username || uri.Password != defaultURI.Password {
+ authority += uri.Username
+
+ if uri.Password != defaultURI.Password {
+ authority += ":" + uri.Password
+ }
+
+ authority += "@"
+ }
+
+ authority += uri.Host
+
+ if defaultPort, found := schemePorts[uri.Scheme]; !found || defaultPort != uri.Port {
+ authority += ":" + strconv.FormatInt(int64(uri.Port), 10)
+ }
+
+ var vhost string
+ if uri.Vhost != defaultURI.Vhost {
+ vhost = uri.Vhost
+ }
+
+ return fmt.Sprintf("%s://%s/%s", uri.Scheme, authority, url.QueryEscape(vhost))
+}
diff --git a/vendor/github.com/streadway/amqp/uri_test.go b/vendor/github.com/streadway/amqp/uri_test.go
new file mode 100644
index 00000000..a957523c
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/uri_test.go
@@ -0,0 +1,325 @@
+package amqp
+
+import (
+ "testing"
+)
+
+// Test matrix defined on http://www.rabbitmq.com/uri-spec.html
+type testURI struct {
+ url string
+ username string
+ password string
+ host string
+ port int
+ vhost string
+ canon string
+}
+
+var uriTests = []testURI{
+ {
+ url: "amqp://user:pass@host:10000/vhost",
+ username: "user",
+ password: "pass",
+ host: "host",
+ port: 10000,
+ vhost: "vhost",
+ canon: "amqp://user:pass@host:10000/vhost",
+ },
+
+ {
+ url: "amqp://",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: defaultURI.Host,
+ port: defaultURI.Port,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://localhost/",
+ },
+
+ {
+ url: "amqp://:@/",
+ username: "",
+ password: "",
+ host: defaultURI.Host,
+ port: defaultURI.Port,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://:@localhost/",
+ },
+
+ {
+ url: "amqp://user@",
+ username: "user",
+ password: defaultURI.Password,
+ host: defaultURI.Host,
+ port: defaultURI.Port,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://user@localhost/",
+ },
+
+ {
+ url: "amqp://user:pass@",
+ username: "user",
+ password: "pass",
+ host: defaultURI.Host,
+ port: defaultURI.Port,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://user:pass@localhost/",
+ },
+
+ {
+ url: "amqp://guest:pass@",
+ username: "guest",
+ password: "pass",
+ host: defaultURI.Host,
+ port: defaultURI.Port,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://guest:pass@localhost/",
+ },
+
+ {
+ url: "amqp://host",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: "host",
+ port: defaultURI.Port,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://host/",
+ },
+
+ {
+ url: "amqp://:10000",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: defaultURI.Host,
+ port: 10000,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://localhost:10000/",
+ },
+
+ {
+ url: "amqp:///vhost",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: defaultURI.Host,
+ port: defaultURI.Port,
+ vhost: "vhost",
+ canon: "amqp://localhost/vhost",
+ },
+
+ {
+ url: "amqp://host/",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: "host",
+ port: defaultURI.Port,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://host/",
+ },
+
+ {
+ url: "amqp://host/%2F",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: "host",
+ port: defaultURI.Port,
+ vhost: "/",
+ canon: "amqp://host/",
+ },
+
+ {
+ url: "amqp://host/%2F%2F",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: "host",
+ port: defaultURI.Port,
+ vhost: "//",
+ canon: "amqp://host/%2F%2F",
+ },
+
+ {
+ url: "amqp://host/%2Fslash%2F",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: "host",
+ port: defaultURI.Port,
+ vhost: "/slash/",
+ canon: "amqp://host/%2Fslash%2F",
+ },
+
+ {
+ url: "amqp://192.168.1.1:1000/",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: "192.168.1.1",
+ port: 1000,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://192.168.1.1:1000/",
+ },
+
+ {
+ url: "amqp://[::1]",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: "[::1]",
+ port: defaultURI.Port,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://[::1]/",
+ },
+
+ {
+ url: "amqp://[::1]:1000",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: "[::1]",
+ port: 1000,
+ vhost: defaultURI.Vhost,
+ canon: "amqp://[::1]:1000/",
+ },
+
+ {
+ url: "amqps:///",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: defaultURI.Host,
+ port: schemePorts["amqps"],
+ vhost: defaultURI.Vhost,
+ canon: "amqps://localhost/",
+ },
+
+ {
+ url: "amqps://host:1000/",
+ username: defaultURI.Username,
+ password: defaultURI.Password,
+ host: "host",
+ port: 1000,
+ vhost: defaultURI.Vhost,
+ canon: "amqps://host:1000/",
+ },
+}
+
+func TestURISpec(t *testing.T) {
+ for _, test := range uriTests {
+ u, err := ParseURI(test.url)
+ if err != nil {
+ t.Fatal("Could not parse spec URI: ", test.url, " err: ", err)
+ }
+
+ if test.username != u.Username {
+ t.Error("For: ", test.url, " usernames do not match. want: ", test.username, " got: ", u.Username)
+ }
+
+ if test.password != u.Password {
+ t.Error("For: ", test.url, " passwords do not match. want: ", test.password, " got: ", u.Password)
+ }
+
+ if test.host != u.Host {
+ t.Error("For: ", test.url, " hosts do not match. want: ", test.host, " got: ", u.Host)
+ }
+
+ if test.port != u.Port {
+ t.Error("For: ", test.url, " ports do not match. want: ", test.port, " got: ", u.Port)
+ }
+
+ if test.vhost != u.Vhost {
+ t.Error("For: ", test.url, " vhosts do not match. want: ", test.vhost, " got: ", u.Vhost)
+ }
+
+ if test.canon != u.String() {
+ t.Error("For: ", test.url, " canonical string does not match. want: ", test.canon, " got: ", u.String())
+ }
+ }
+}
+
+func TestURIUnknownScheme(t *testing.T) {
+ if _, err := ParseURI("http://example.com/"); err == nil {
+ t.Fatal("Expected error when parsing non-amqp scheme")
+ }
+}
+
+func TestURIScheme(t *testing.T) {
+ if _, err := ParseURI("amqp://example.com/"); err != nil {
+ t.Fatalf("Expected to parse amqp scheme, got %v", err)
+ }
+
+ if _, err := ParseURI("amqps://example.com/"); err != nil {
+ t.Fatalf("Expected to parse amqps scheme, got %v", err)
+ }
+}
+
+func TestURIWhitespace(t *testing.T) {
+ if _, err := ParseURI("amqp://admin:PASSWORD@rabbitmq-service/ -http_port=8080"); err == nil {
+ t.Fatal("Expected to fail if URI contains whitespace")
+ }
+}
+
+func TestURIDefaults(t *testing.T) {
+ url := "amqp://"
+ uri, err := ParseURI(url)
+ if err != nil {
+ t.Fatal("Could not parse")
+ }
+
+ if uri.String() != "amqp://localhost/" {
+ t.Fatal("Defaults not encoded properly got:", uri.String())
+ }
+}
+
+func TestURIComplete(t *testing.T) {
+ url := "amqp://bob:dobbs@foo.bar:5678/private"
+ uri, err := ParseURI(url)
+ if err != nil {
+ t.Fatal("Could not parse")
+ }
+
+ if uri.String() != url {
+ t.Fatal("Defaults not encoded properly want:", url, " got:", uri.String())
+ }
+}
+
+func TestURIDefaultPortAmqpNotIncluded(t *testing.T) {
+ url := "amqp://foo.bar:5672/"
+ uri, err := ParseURI(url)
+ if err != nil {
+ t.Fatal("Could not parse")
+ }
+
+ if uri.String() != "amqp://foo.bar/" {
+ t.Fatal("Defaults not encoded properly got:", uri.String())
+ }
+}
+
+func TestURIDefaultPortAmqp(t *testing.T) {
+ url := "amqp://foo.bar/"
+ uri, err := ParseURI(url)
+ if err != nil {
+ t.Fatal("Could not parse")
+ }
+
+ if uri.Port != 5672 {
+ t.Fatal("Default port not correct for amqp, got:", uri.Port)
+ }
+}
+
+func TestURIDefaultPortAmqpsNotIncludedInString(t *testing.T) {
+ url := "amqps://foo.bar:5671/"
+ uri, err := ParseURI(url)
+ if err != nil {
+ t.Fatal("Could not parse")
+ }
+
+ if uri.String() != "amqps://foo.bar/" {
+ t.Fatal("Defaults not encoded properly got:", uri.String())
+ }
+}
+
+func TestURIDefaultPortAmqps(t *testing.T) {
+ url := "amqps://foo.bar/"
+ uri, err := ParseURI(url)
+ if err != nil {
+ t.Fatal("Could not parse")
+ }
+
+ if uri.Port != 5671 {
+ t.Fatal("Default port not correct for amqps, got:", uri.Port)
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/write.go b/vendor/github.com/streadway/amqp/write.go
new file mode 100644
index 00000000..58ed20d6
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/write.go
@@ -0,0 +1,411 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "io"
+ "math"
+ "time"
+)
+
+func (w *writer) WriteFrame(frame frame) (err error) {
+ if err = frame.write(w.w); err != nil {
+ return
+ }
+
+ if buf, ok := w.w.(*bufio.Writer); ok {
+ err = buf.Flush()
+ }
+
+ return
+}
+
+func (f *methodFrame) write(w io.Writer) (err error) {
+ var payload bytes.Buffer
+
+ if f.Method == nil {
+ return errors.New("malformed frame: missing method")
+ }
+
+ class, method := f.Method.id()
+
+ if err = binary.Write(&payload, binary.BigEndian, class); err != nil {
+ return
+ }
+
+ if err = binary.Write(&payload, binary.BigEndian, method); err != nil {
+ return
+ }
+
+ if err = f.Method.write(&payload); err != nil {
+ return
+ }
+
+ return writeFrame(w, frameMethod, f.ChannelId, payload.Bytes())
+}
+
+// Heartbeat
+//
+// Payload is empty
+func (f *heartbeatFrame) write(w io.Writer) (err error) {
+ return writeFrame(w, frameHeartbeat, f.ChannelId, []byte{})
+}
+
+// CONTENT HEADER
+// 0 2 4 12 14
+// +----------+--------+-----------+----------------+------------- - -
+// | class-id | weight | body size | property flags | property list...
+// +----------+--------+-----------+----------------+------------- - -
+// short short long long short remainder...
+//
+func (f *headerFrame) write(w io.Writer) (err error) {
+ var payload bytes.Buffer
+ var zeroTime time.Time
+
+ if err = binary.Write(&payload, binary.BigEndian, f.ClassId); err != nil {
+ return
+ }
+
+ if err = binary.Write(&payload, binary.BigEndian, f.weight); err != nil {
+ return
+ }
+
+ if err = binary.Write(&payload, binary.BigEndian, f.Size); err != nil {
+ return
+ }
+
+ // First pass will build the mask to be serialized, second pass will serialize
+ // each of the fields that appear in the mask.
+
+ var mask uint16
+
+ if len(f.Properties.ContentType) > 0 {
+ mask = mask | flagContentType
+ }
+ if len(f.Properties.ContentEncoding) > 0 {
+ mask = mask | flagContentEncoding
+ }
+ if f.Properties.Headers != nil && len(f.Properties.Headers) > 0 {
+ mask = mask | flagHeaders
+ }
+ if f.Properties.DeliveryMode > 0 {
+ mask = mask | flagDeliveryMode
+ }
+ if f.Properties.Priority > 0 {
+ mask = mask | flagPriority
+ }
+ if len(f.Properties.CorrelationId) > 0 {
+ mask = mask | flagCorrelationId
+ }
+ if len(f.Properties.ReplyTo) > 0 {
+ mask = mask | flagReplyTo
+ }
+ if len(f.Properties.Expiration) > 0 {
+ mask = mask | flagExpiration
+ }
+ if len(f.Properties.MessageId) > 0 {
+ mask = mask | flagMessageId
+ }
+ if f.Properties.Timestamp != zeroTime {
+ mask = mask | flagTimestamp
+ }
+ if len(f.Properties.Type) > 0 {
+ mask = mask | flagType
+ }
+ if len(f.Properties.UserId) > 0 {
+ mask = mask | flagUserId
+ }
+ if len(f.Properties.AppId) > 0 {
+ mask = mask | flagAppId
+ }
+
+ if err = binary.Write(&payload, binary.BigEndian, mask); err != nil {
+ return
+ }
+
+ if hasProperty(mask, flagContentType) {
+ if err = writeShortstr(&payload, f.Properties.ContentType); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagContentEncoding) {
+ if err = writeShortstr(&payload, f.Properties.ContentEncoding); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagHeaders) {
+ if err = writeTable(&payload, f.Properties.Headers); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagDeliveryMode) {
+ if err = binary.Write(&payload, binary.BigEndian, f.Properties.DeliveryMode); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagPriority) {
+ if err = binary.Write(&payload, binary.BigEndian, f.Properties.Priority); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagCorrelationId) {
+ if err = writeShortstr(&payload, f.Properties.CorrelationId); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagReplyTo) {
+ if err = writeShortstr(&payload, f.Properties.ReplyTo); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagExpiration) {
+ if err = writeShortstr(&payload, f.Properties.Expiration); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagMessageId) {
+ if err = writeShortstr(&payload, f.Properties.MessageId); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagTimestamp) {
+ if err = binary.Write(&payload, binary.BigEndian, uint64(f.Properties.Timestamp.Unix())); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagType) {
+ if err = writeShortstr(&payload, f.Properties.Type); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagUserId) {
+ if err = writeShortstr(&payload, f.Properties.UserId); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagAppId) {
+ if err = writeShortstr(&payload, f.Properties.AppId); err != nil {
+ return
+ }
+ }
+
+ return writeFrame(w, frameHeader, f.ChannelId, payload.Bytes())
+}
+
+// Body
+//
+// Payload is one byterange from the full body who's size is declared in the
+// Header frame
+func (f *bodyFrame) write(w io.Writer) (err error) {
+ return writeFrame(w, frameBody, f.ChannelId, f.Body)
+}
+
+func writeFrame(w io.Writer, typ uint8, channel uint16, payload []byte) (err error) {
+ end := []byte{frameEnd}
+ size := uint(len(payload))
+
+ _, err = w.Write([]byte{
+ byte(typ),
+ byte((channel & 0xff00) >> 8),
+ byte((channel & 0x00ff) >> 0),
+ byte((size & 0xff000000) >> 24),
+ byte((size & 0x00ff0000) >> 16),
+ byte((size & 0x0000ff00) >> 8),
+ byte((size & 0x000000ff) >> 0),
+ })
+
+ if err != nil {
+ return
+ }
+
+ if _, err = w.Write(payload); err != nil {
+ return
+ }
+
+ if _, err = w.Write(end); err != nil {
+ return
+ }
+
+ return
+}
+
+func writeShortstr(w io.Writer, s string) (err error) {
+ b := []byte(s)
+
+ var length = uint8(len(b))
+
+ if err = binary.Write(w, binary.BigEndian, length); err != nil {
+ return
+ }
+
+ if _, err = w.Write(b[:length]); err != nil {
+ return
+ }
+
+ return
+}
+
+func writeLongstr(w io.Writer, s string) (err error) {
+ b := []byte(s)
+
+ var length = uint32(len(b))
+
+ if err = binary.Write(w, binary.BigEndian, length); err != nil {
+ return
+ }
+
+ if _, err = w.Write(b[:length]); err != nil {
+ return
+ }
+
+ return
+}
+
+/*
+'A': []interface{}
+'D': Decimal
+'F': Table
+'I': int32
+'S': string
+'T': time.Time
+'V': nil
+'b': byte
+'d': float64
+'f': float32
+'l': int64
+'s': int16
+'t': bool
+'x': []byte
+*/
+func writeField(w io.Writer, value interface{}) (err error) {
+ var buf [9]byte
+ var enc []byte
+
+ switch v := value.(type) {
+ case bool:
+ buf[0] = 't'
+ if v {
+ buf[1] = byte(1)
+ } else {
+ buf[1] = byte(0)
+ }
+ enc = buf[:2]
+
+ case byte:
+ buf[0] = 'b'
+ buf[1] = byte(v)
+ enc = buf[:2]
+
+ case int16:
+ buf[0] = 's'
+ binary.BigEndian.PutUint16(buf[1:3], uint16(v))
+ enc = buf[:3]
+
+ case int32:
+ buf[0] = 'I'
+ binary.BigEndian.PutUint32(buf[1:5], uint32(v))
+ enc = buf[:5]
+
+ case int64:
+ buf[0] = 'l'
+ binary.BigEndian.PutUint64(buf[1:9], uint64(v))
+ enc = buf[:9]
+
+ case float32:
+ buf[0] = 'f'
+ binary.BigEndian.PutUint32(buf[1:5], math.Float32bits(v))
+ enc = buf[:5]
+
+ case float64:
+ buf[0] = 'd'
+ binary.BigEndian.PutUint64(buf[1:9], math.Float64bits(v))
+ enc = buf[:9]
+
+ case Decimal:
+ buf[0] = 'D'
+ buf[1] = byte(v.Scale)
+ binary.BigEndian.PutUint32(buf[2:6], uint32(v.Value))
+ enc = buf[:6]
+
+ case string:
+ buf[0] = 'S'
+ binary.BigEndian.PutUint32(buf[1:5], uint32(len(v)))
+ enc = append(buf[:5], []byte(v)...)
+
+ case []interface{}: // field-array
+ buf[0] = 'A'
+
+ sec := new(bytes.Buffer)
+ for _, val := range v {
+ if err = writeField(sec, val); err != nil {
+ return
+ }
+ }
+
+ binary.BigEndian.PutUint32(buf[1:5], uint32(sec.Len()))
+ if _, err = w.Write(buf[:5]); err != nil {
+ return
+ }
+
+ if _, err = w.Write(sec.Bytes()); err != nil {
+ return
+ }
+
+ return
+
+ case time.Time:
+ buf[0] = 'T'
+ binary.BigEndian.PutUint64(buf[1:9], uint64(v.Unix()))
+ enc = buf[:9]
+
+ case Table:
+ if _, err = w.Write([]byte{'F'}); err != nil {
+ return
+ }
+ return writeTable(w, v)
+
+ case []byte:
+ buf[0] = 'x'
+ binary.BigEndian.PutUint32(buf[1:5], uint32(len(v)))
+ if _, err = w.Write(buf[0:5]); err != nil {
+ return
+ }
+ if _, err = w.Write(v); err != nil {
+ return
+ }
+ return
+
+ case nil:
+ buf[0] = 'V'
+ enc = buf[:1]
+
+ default:
+ return ErrFieldType
+ }
+
+ _, err = w.Write(enc)
+
+ return
+}
+
+func writeTable(w io.Writer, table Table) (err error) {
+ var buf bytes.Buffer
+
+ for key, val := range table {
+ if err = writeShortstr(&buf, key); err != nil {
+ return
+ }
+ if err = writeField(&buf, val); err != nil {
+ return
+ }
+ }
+
+ return writeLongstr(w, string(buf.Bytes()))
+}