switch to github.com/rabbitmq/amqp091-go module

This commit is contained in:
CrazyMax
2024-12-15 00:43:49 +01:00
parent 2a4a8863f3
commit 12610c46bc
41 changed files with 2506 additions and 895 deletions

2
go.mod
View File

@@ -34,10 +34,10 @@ require (
github.com/panjf2000/ants/v2 v2.10.0 github.com/panjf2000/ants/v2 v2.10.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.7.0 github.com/pkg/profile v1.7.0
github.com/rabbitmq/amqp091-go v1.10.0
github.com/rs/zerolog v1.33.0 github.com/rs/zerolog v1.33.0
github.com/russross/blackfriday/v2 v2.1.0 github.com/russross/blackfriday/v2 v2.1.0
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/streadway/amqp v1.1.0
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
github.com/tidwall/pretty v1.2.1 github.com/tidwall/pretty v1.2.1
go.etcd.io/bbolt v1.3.10 go.etcd.io/bbolt v1.3.10

6
go.sum
View File

@@ -281,6 +281,8 @@ github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4Ds
github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -301,8 +303,6 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM=
github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -348,6 +348,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20181029175232-7e6ffbd03851/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029175232-7e6ffbd03851/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

View File

@@ -7,7 +7,7 @@ import (
"github.com/crazy-max/diun/v4/internal/msg" "github.com/crazy-max/diun/v4/internal/msg"
"github.com/crazy-max/diun/v4/internal/notif/notifier" "github.com/crazy-max/diun/v4/internal/notif/notifier"
"github.com/crazy-max/diun/v4/pkg/utl" "github.com/crazy-max/diun/v4/pkg/utl"
"github.com/streadway/amqp" amqp "github.com/rabbitmq/amqp091-go"
) )
// Client represents an active amqp notification object // Client represents an active amqp notification object

6
vendor/github.com/rabbitmq/amqp091-go/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,6 @@
certs/*
spec/spec
examples/simple-consumer/simple-consumer
examples/simple-producer/simple-producer
.idea/

3
vendor/github.com/rabbitmq/amqp091-go/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,3 @@
run:
build-tags:
- integration

363
vendor/github.com/rabbitmq/amqp091-go/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,363 @@
# Changelog
## [v1.10.0](https://github.com/rabbitmq/amqp091-go/tree/v1.10.0) (2024-05-08)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.9.0...v1.10.0)
**Implemented enhancements:**
- Undeprecate non-context publish functions [\#259](https://github.com/rabbitmq/amqp091-go/pull/259) ([Zerpet](https://github.com/Zerpet))
- Update Go directive [\#257](https://github.com/rabbitmq/amqp091-go/pull/257) ([Zerpet](https://github.com/Zerpet))
**Fixed bugs:**
- republishing on reconnect bug in the example [\#249](https://github.com/rabbitmq/amqp091-go/issues/249)
- Channel Notify Close not receive event when connection is closed by RMQ server. [\#241](https://github.com/rabbitmq/amqp091-go/issues/241)
- Inconsistent documentation [\#231](https://github.com/rabbitmq/amqp091-go/issues/231)
- Data race in the client example [\#72](https://github.com/rabbitmq/amqp091-go/issues/72)
- Fix string function of URI [\#258](https://github.com/rabbitmq/amqp091-go/pull/258) ([Zerpet](https://github.com/Zerpet))
**Closed issues:**
- Documentation needed \(`PublishWithContext` does not use context\) [\#195](https://github.com/rabbitmq/amqp091-go/issues/195)
- concurrent dispatch data race [\#226](https://github.com/rabbitmq/amqp091-go/issues/226)
**Merged pull requests:**
- Fix data race in example [\#260](https://github.com/rabbitmq/amqp091-go/pull/260) ([Zerpet](https://github.com/Zerpet))
- Address CodeQL warning [\#252](https://github.com/rabbitmq/amqp091-go/pull/252) ([lukebakken](https://github.com/lukebakken))
- Add support for additional AMQP URI query parameters [\#251](https://github.com/rabbitmq/amqp091-go/pull/251) ([vilius-g](https://github.com/vilius-g))
- Example fix [\#250](https://github.com/rabbitmq/amqp091-go/pull/250) ([Boris-Plato](https://github.com/Boris-Plato))
- Increasing the code coverage [\#248](https://github.com/rabbitmq/amqp091-go/pull/248) ([edercarloscosta](https://github.com/edercarloscosta))
- Use correct mutex to guard confirms.published [\#240](https://github.com/rabbitmq/amqp091-go/pull/240) ([hjr265](https://github.com/hjr265))
- Documenting Publishing.Expiration usage [\#232](https://github.com/rabbitmq/amqp091-go/pull/232) ([niksteff](https://github.com/niksteff))
- fix comment typo in example\_client\_test.go [\#228](https://github.com/rabbitmq/amqp091-go/pull/228) ([wisaTong](https://github.com/wisaTong))
- Bump go.uber.org/goleak from 1.2.1 to 1.3.0 [\#227](https://github.com/rabbitmq/amqp091-go/pull/227) ([dependabot[bot]](https://github.com/apps/dependabot))
## [v1.9.0](https://github.com/rabbitmq/amqp091-go/tree/v1.9.0) (2023-10-02)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.8.1...v1.9.0)
**Implemented enhancements:**
- Use of buffered delivery channels when prefetch\_count is not null [\#200](https://github.com/rabbitmq/amqp091-go/issues/200)
**Fixed bugs:**
- connection block when write connection reset by peer [\#222](https://github.com/rabbitmq/amqp091-go/issues/222)
- Test failure on 32bit architectures [\#202](https://github.com/rabbitmq/amqp091-go/issues/202)
**Closed issues:**
- Add a constant to set consumer timeout as queue argument [\#201](https://github.com/rabbitmq/amqp091-go/issues/201)
- Add a constant for CQ version [\#199](https://github.com/rabbitmq/amqp091-go/issues/199)
- Examples may need to be updated after \#140 [\#153](https://github.com/rabbitmq/amqp091-go/issues/153)
**Merged pull requests:**
- Update spec091.go [\#224](https://github.com/rabbitmq/amqp091-go/pull/224) ([pinkfish](https://github.com/pinkfish))
- Closes 222 [\#223](https://github.com/rabbitmq/amqp091-go/pull/223) ([yywing](https://github.com/yywing))
- Update write.go [\#221](https://github.com/rabbitmq/amqp091-go/pull/221) ([pinkfish](https://github.com/pinkfish))
- Bump versions [\#219](https://github.com/rabbitmq/amqp091-go/pull/219) ([lukebakken](https://github.com/lukebakken))
- remove extra word 'accept' from ExchangeDeclare description [\#217](https://github.com/rabbitmq/amqp091-go/pull/217) ([a-sabzian](https://github.com/a-sabzian))
- Misc Windows CI updates [\#216](https://github.com/rabbitmq/amqp091-go/pull/216) ([lukebakken](https://github.com/lukebakken))
- Stop using deprecated Publish function [\#207](https://github.com/rabbitmq/amqp091-go/pull/207) ([Zerpet](https://github.com/Zerpet))
- Constant for consumer timeout queue argument [\#206](https://github.com/rabbitmq/amqp091-go/pull/206) ([Zerpet](https://github.com/Zerpet))
- Add a constant for CQ v2 queue argument [\#205](https://github.com/rabbitmq/amqp091-go/pull/205) ([Zerpet](https://github.com/Zerpet))
- Fix example for 32-bit compatibility [\#204](https://github.com/rabbitmq/amqp091-go/pull/204) ([Zerpet](https://github.com/Zerpet))
- Fix to increase timeout milliseconds since it's too tight [\#203](https://github.com/rabbitmq/amqp091-go/pull/203) ([t2y](https://github.com/t2y))
- Add Channel.ConsumeWithContext to be able to cancel delivering [\#192](https://github.com/rabbitmq/amqp091-go/pull/192) ([t2y](https://github.com/t2y))
## [v1.8.1](https://github.com/rabbitmq/amqp091-go/tree/v1.8.1) (2023-05-04)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.8.0...v1.8.1)
**Fixed bugs:**
- Fixed incorrect version reported in client properties [52ce2efd03c53dcf77d5496977da46840e9abd24](https://github.com/rabbitmq/amqp091-go/commit/52ce2efd03c53dcf77d5496977da46840e9abd24)
**Merged pull requests:**
- Fix Example Client not reconnecting [\#186](https://github.com/rabbitmq/amqp091-go/pull/186) ([frankfil](https://github.com/frankfil))
## [v1.8.0](https://github.com/rabbitmq/amqp091-go/tree/v1.8.0) (2023-03-21)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.7.0...v1.8.0)
**Closed issues:**
- memory leak [\#179](https://github.com/rabbitmq/amqp091-go/issues/179)
- the publishWithContext interface will not return when it times out [\#178](https://github.com/rabbitmq/amqp091-go/issues/178)
**Merged pull requests:**
- Fix race condition on confirms [\#183](https://github.com/rabbitmq/amqp091-go/pull/183) ([calloway-jacob](https://github.com/calloway-jacob))
- Add a CloseDeadline function to Connection [\#181](https://github.com/rabbitmq/amqp091-go/pull/181) ([Zerpet](https://github.com/Zerpet))
- Fix memory leaks [\#180](https://github.com/rabbitmq/amqp091-go/pull/180) ([GXKe](https://github.com/GXKe))
- Bump go.uber.org/goleak from 1.2.0 to 1.2.1 [\#177](https://github.com/rabbitmq/amqp091-go/pull/177) ([dependabot[bot]](https://github.com/apps/dependabot))
## [v1.7.0](https://github.com/rabbitmq/amqp091-go/tree/v1.7.0) (2023-02-09)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.6.1...v1.7.0)
**Closed issues:**
- \#31 resurfacing \(?\) [\#170](https://github.com/rabbitmq/amqp091-go/issues/170)
- Deprecate QueueInspect [\#167](https://github.com/rabbitmq/amqp091-go/issues/167)
- v1.6.0 causing rabbit connection errors [\#160](https://github.com/rabbitmq/amqp091-go/issues/160)
**Merged pull requests:**
- Set channels and allocator to nil in shutdown [\#172](https://github.com/rabbitmq/amqp091-go/pull/172) ([lukebakken](https://github.com/lukebakken))
- Fix racing in Open [\#171](https://github.com/rabbitmq/amqp091-go/pull/171) ([Zerpet](https://github.com/Zerpet))
- adding go 1.20 to tests [\#169](https://github.com/rabbitmq/amqp091-go/pull/169) ([halilylm](https://github.com/halilylm))
- Deprecate the QueueInspect function [\#168](https://github.com/rabbitmq/amqp091-go/pull/168) ([lukebakken](https://github.com/lukebakken))
- Check if channel is nil before updating it [\#150](https://github.com/rabbitmq/amqp091-go/pull/150) ([julienschmidt](https://github.com/julienschmidt))
## [v1.6.1](https://github.com/rabbitmq/amqp091-go/tree/v1.6.1) (2023-02-01)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.6.1-rc.2...v1.6.1)
**Merged pull requests:**
- Update Makefile targets related to RabbitMQ [\#163](https://github.com/rabbitmq/amqp091-go/pull/163) ([Zerpet](https://github.com/Zerpet))
## [v1.6.1-rc.2](https://github.com/rabbitmq/amqp091-go/tree/v1.6.1-rc.2) (2023-01-31)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.6.1-rc.1...v1.6.1-rc.2)
**Merged pull requests:**
- Do not overly protect writes [\#162](https://github.com/rabbitmq/amqp091-go/pull/162) ([lukebakken](https://github.com/lukebakken))
## [v1.6.1-rc.1](https://github.com/rabbitmq/amqp091-go/tree/v1.6.1-rc.1) (2023-01-31)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.6.0...v1.6.1-rc.1)
**Closed issues:**
- Calling Channel\(\) on an empty connection panics [\#148](https://github.com/rabbitmq/amqp091-go/issues/148)
**Merged pull requests:**
- Ensure flush happens and correctly lock connection for a series of unflushed writes [\#161](https://github.com/rabbitmq/amqp091-go/pull/161) ([lukebakken](https://github.com/lukebakken))
## [v1.6.0](https://github.com/rabbitmq/amqp091-go/tree/v1.6.0) (2023-01-20)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.5.0...v1.6.0)
**Implemented enhancements:**
- Add constants for Queue arguments [\#145](https://github.com/rabbitmq/amqp091-go/pull/145) ([Zerpet](https://github.com/Zerpet))
**Closed issues:**
- README not up to date [\#154](https://github.com/rabbitmq/amqp091-go/issues/154)
- Allow re-using default connection config \(custom properties\) [\#152](https://github.com/rabbitmq/amqp091-go/issues/152)
- Rename package name to amqp in V2 [\#151](https://github.com/rabbitmq/amqp091-go/issues/151)
- Helper types to declare quorum queues [\#144](https://github.com/rabbitmq/amqp091-go/issues/144)
- Inefficient use of buffers reduces potential throughput for basicPublish with small messages. [\#141](https://github.com/rabbitmq/amqp091-go/issues/141)
- bug, close cause panic [\#130](https://github.com/rabbitmq/amqp091-go/issues/130)
- Publishing Headers are unable to store Table with slice values [\#125](https://github.com/rabbitmq/amqp091-go/issues/125)
- Example client can deadlock in Close due to unconsumed confirmations [\#122](https://github.com/rabbitmq/amqp091-go/issues/122)
- SAC not working properly [\#106](https://github.com/rabbitmq/amqp091-go/issues/106)
**Merged pull requests:**
- Add automatic CHANGELOG.md generation [\#158](https://github.com/rabbitmq/amqp091-go/pull/158) ([lukebakken](https://github.com/lukebakken))
- Supply library-defined props with NewConnectionProperties [\#157](https://github.com/rabbitmq/amqp091-go/pull/157) ([slagiewka](https://github.com/slagiewka))
- Fix linter warnings [\#156](https://github.com/rabbitmq/amqp091-go/pull/156) ([Zerpet](https://github.com/Zerpet))
- Remove outdated information from README [\#155](https://github.com/rabbitmq/amqp091-go/pull/155) ([scriptcoded](https://github.com/scriptcoded))
- Add example producer using DeferredConfirm [\#149](https://github.com/rabbitmq/amqp091-go/pull/149) ([Zerpet](https://github.com/Zerpet))
- Ensure code is formatted [\#147](https://github.com/rabbitmq/amqp091-go/pull/147) ([lukebakken](https://github.com/lukebakken))
- Fix inefficient use of buffers that reduces the potential throughput of basicPublish [\#142](https://github.com/rabbitmq/amqp091-go/pull/142) ([fadams](https://github.com/fadams))
- Do not embed context in DeferredConfirmation [\#140](https://github.com/rabbitmq/amqp091-go/pull/140) ([tie](https://github.com/tie))
- Add constant for default exchange [\#139](https://github.com/rabbitmq/amqp091-go/pull/139) ([marlongerson](https://github.com/marlongerson))
- Fix indentation and remove unnecessary instructions [\#138](https://github.com/rabbitmq/amqp091-go/pull/138) ([alraujo](https://github.com/alraujo))
- Remove unnecessary instruction [\#135](https://github.com/rabbitmq/amqp091-go/pull/135) ([alraujo](https://github.com/alraujo))
- Fix example client to avoid deadlock in Close [\#123](https://github.com/rabbitmq/amqp091-go/pull/123) ([Zerpet](https://github.com/Zerpet))
- Bump go.uber.org/goleak from 1.1.12 to 1.2.0 [\#116](https://github.com/rabbitmq/amqp091-go/pull/116) ([dependabot[bot]](https://github.com/apps/dependabot))
## [v1.5.0](https://github.com/rabbitmq/amqp091-go/tree/v1.5.0) (2022-09-07)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.4.0...v1.5.0)
**Implemented enhancements:**
- Provide a friendly way to set connection name [\#105](https://github.com/rabbitmq/amqp091-go/issues/105)
**Closed issues:**
- Support connection.update-secret [\#107](https://github.com/rabbitmq/amqp091-go/issues/107)
- Example Client: Implementation of a Consumer with reconnection support [\#40](https://github.com/rabbitmq/amqp091-go/issues/40)
**Merged pull requests:**
- use PublishWithContext instead of Publish [\#115](https://github.com/rabbitmq/amqp091-go/pull/115) ([Gsantomaggio](https://github.com/Gsantomaggio))
- Add support for connection.update-secret [\#114](https://github.com/rabbitmq/amqp091-go/pull/114) ([Zerpet](https://github.com/Zerpet))
- Remove warning on RabbitMQ tutorials in go [\#113](https://github.com/rabbitmq/amqp091-go/pull/113) ([ChunyiLyu](https://github.com/ChunyiLyu))
- Update AMQP Spec [\#110](https://github.com/rabbitmq/amqp091-go/pull/110) ([Zerpet](https://github.com/Zerpet))
- Add an example of reliable consumer [\#109](https://github.com/rabbitmq/amqp091-go/pull/109) ([Zerpet](https://github.com/Zerpet))
- Add convenience function to set connection name [\#108](https://github.com/rabbitmq/amqp091-go/pull/108) ([Zerpet](https://github.com/Zerpet))
## [v1.4.0](https://github.com/rabbitmq/amqp091-go/tree/v1.4.0) (2022-07-19)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.3.4...v1.4.0)
**Closed issues:**
- target machine actively refused connection [\#99](https://github.com/rabbitmq/amqp091-go/issues/99)
- 504 channel/connection is not open error occurred in multiple connection with same rabbitmq service [\#97](https://github.com/rabbitmq/amqp091-go/issues/97)
- Add possible cancel of DeferredConfirmation [\#92](https://github.com/rabbitmq/amqp091-go/issues/92)
- Documentation [\#89](https://github.com/rabbitmq/amqp091-go/issues/89)
- Channel Close gets stuck after closing a connection \(via management UI\) [\#88](https://github.com/rabbitmq/amqp091-go/issues/88)
- this library has same issue [\#83](https://github.com/rabbitmq/amqp091-go/issues/83)
- Provide a logging interface [\#81](https://github.com/rabbitmq/amqp091-go/issues/81)
- 1.4.0 release checklist [\#77](https://github.com/rabbitmq/amqp091-go/issues/77)
- Data race in the client example [\#72](https://github.com/rabbitmq/amqp091-go/issues/72)
- reader go routine hangs and leaks when Connection.Close\(\) is called multiple times [\#69](https://github.com/rabbitmq/amqp091-go/issues/69)
- Support auto-reconnect and cluster [\#65](https://github.com/rabbitmq/amqp091-go/issues/65)
- Connection/Channel Deadlock [\#32](https://github.com/rabbitmq/amqp091-go/issues/32)
- Closing connection and/or channel hangs NotifyPublish is used [\#21](https://github.com/rabbitmq/amqp091-go/issues/21)
- Consumer channel isn't closed in the event of unexpected disconnection [\#18](https://github.com/rabbitmq/amqp091-go/issues/18)
**Merged pull requests:**
- fix race condition with context close and confirm at the same time on DeferredConfirmation. [\#101](https://github.com/rabbitmq/amqp091-go/pull/101) ([sapk](https://github.com/sapk))
- Add build TLS config from URI [\#98](https://github.com/rabbitmq/amqp091-go/pull/98) ([reddec](https://github.com/reddec))
- Use context for Publish methods [\#96](https://github.com/rabbitmq/amqp091-go/pull/96) ([sapk](https://github.com/sapk))
- Added function to get the remote peer's IP address \(conn.RemoteAddr\(\)\) [\#95](https://github.com/rabbitmq/amqp091-go/pull/95) ([rabb1t](https://github.com/rabb1t))
- Update connection documentation [\#90](https://github.com/rabbitmq/amqp091-go/pull/90) ([Zerpet](https://github.com/Zerpet))
- Revert test to demonstrate actual bug [\#87](https://github.com/rabbitmq/amqp091-go/pull/87) ([lukebakken](https://github.com/lukebakken))
- Minor improvements to examples [\#86](https://github.com/rabbitmq/amqp091-go/pull/86) ([lukebakken](https://github.com/lukebakken))
- Do not skip flaky test in CI [\#85](https://github.com/rabbitmq/amqp091-go/pull/85) ([lukebakken](https://github.com/lukebakken))
- Add logging [\#84](https://github.com/rabbitmq/amqp091-go/pull/84) ([lukebakken](https://github.com/lukebakken))
- Add a win32 build [\#82](https://github.com/rabbitmq/amqp091-go/pull/82) ([lukebakken](https://github.com/lukebakken))
- channel: return nothing instead of always a nil-error in receive methods [\#80](https://github.com/rabbitmq/amqp091-go/pull/80) ([fho](https://github.com/fho))
- update the contributing & readme files, improve makefile [\#79](https://github.com/rabbitmq/amqp091-go/pull/79) ([fho](https://github.com/fho))
- Fix lint errors [\#78](https://github.com/rabbitmq/amqp091-go/pull/78) ([lukebakken](https://github.com/lukebakken))
- ci: run golangci-lint [\#76](https://github.com/rabbitmq/amqp091-go/pull/76) ([fho](https://github.com/fho))
- ci: run test via make & remove travis CI config [\#75](https://github.com/rabbitmq/amqp091-go/pull/75) ([fho](https://github.com/fho))
- ci: run tests with race detector [\#74](https://github.com/rabbitmq/amqp091-go/pull/74) ([fho](https://github.com/fho))
- Detect go routine leaks in integration testcases [\#73](https://github.com/rabbitmq/amqp091-go/pull/73) ([fho](https://github.com/fho))
- connection: fix: reader go-routine is leaked on connection close [\#70](https://github.com/rabbitmq/amqp091-go/pull/70) ([fho](https://github.com/fho))
- adding best practises for NotifyPublish for issue\_21 scenario [\#68](https://github.com/rabbitmq/amqp091-go/pull/68) ([DanielePalaia](https://github.com/DanielePalaia))
- Update Go version [\#67](https://github.com/rabbitmq/amqp091-go/pull/67) ([Zerpet](https://github.com/Zerpet))
- Regenerate certs with SHA256 to fix test with Go 1.18+ [\#66](https://github.com/rabbitmq/amqp091-go/pull/66) ([anthonyfok](https://github.com/anthonyfok))
## [v1.3.4](https://github.com/rabbitmq/amqp091-go/tree/v1.3.4) (2022-04-01)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.3.3...v1.3.4)
**Merged pull requests:**
- bump version to 1.3.4 [\#63](https://github.com/rabbitmq/amqp091-go/pull/63) ([DanielePalaia](https://github.com/DanielePalaia))
- updating doc [\#62](https://github.com/rabbitmq/amqp091-go/pull/62) ([DanielePalaia](https://github.com/DanielePalaia))
## [v1.3.3](https://github.com/rabbitmq/amqp091-go/tree/v1.3.3) (2022-04-01)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.3.2...v1.3.3)
**Closed issues:**
- Add Client Version [\#49](https://github.com/rabbitmq/amqp091-go/issues/49)
- OpenTelemetry Propagation [\#22](https://github.com/rabbitmq/amqp091-go/issues/22)
**Merged pull requests:**
- bump buildVersion for release [\#61](https://github.com/rabbitmq/amqp091-go/pull/61) ([DanielePalaia](https://github.com/DanielePalaia))
- adding documentation for notifyClose best pratices [\#60](https://github.com/rabbitmq/amqp091-go/pull/60) ([DanielePalaia](https://github.com/DanielePalaia))
- adding documentation on NotifyClose of connection and channel to enfo… [\#59](https://github.com/rabbitmq/amqp091-go/pull/59) ([DanielePalaia](https://github.com/DanielePalaia))
## [v1.3.2](https://github.com/rabbitmq/amqp091-go/tree/v1.3.2) (2022-03-28)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.3.1...v1.3.2)
**Closed issues:**
- Potential race condition in Connection module [\#31](https://github.com/rabbitmq/amqp091-go/issues/31)
**Merged pull requests:**
- bump versioning to 1.3.2 [\#58](https://github.com/rabbitmq/amqp091-go/pull/58) ([DanielePalaia](https://github.com/DanielePalaia))
## [v1.3.1](https://github.com/rabbitmq/amqp091-go/tree/v1.3.1) (2022-03-25)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.3.0...v1.3.1)
**Closed issues:**
- Possible deadlock on DeferredConfirmation.Wait\(\) [\#46](https://github.com/rabbitmq/amqp091-go/issues/46)
- Call to Delivery.Ack blocks indefinitely in case of disconnection [\#19](https://github.com/rabbitmq/amqp091-go/issues/19)
- Unexpacted behavor of channel.IsClosed\(\) [\#14](https://github.com/rabbitmq/amqp091-go/issues/14)
- A possible dead lock in connection close notification Go channel [\#11](https://github.com/rabbitmq/amqp091-go/issues/11)
**Merged pull requests:**
- These ones were the ones testing Open scenarios. The issue is that Op… [\#57](https://github.com/rabbitmq/amqp091-go/pull/57) ([DanielePalaia](https://github.com/DanielePalaia))
- changing defaultVersion to buildVersion and create a simple change\_ve… [\#54](https://github.com/rabbitmq/amqp091-go/pull/54) ([DanielePalaia](https://github.com/DanielePalaia))
- adding integration test for issue 11 [\#50](https://github.com/rabbitmq/amqp091-go/pull/50) ([DanielePalaia](https://github.com/DanielePalaia))
- Remove the old link product [\#48](https://github.com/rabbitmq/amqp091-go/pull/48) ([Gsantomaggio](https://github.com/Gsantomaggio))
- Fix deadlock on DeferredConfirmations [\#47](https://github.com/rabbitmq/amqp091-go/pull/47) ([SpencerTorres](https://github.com/SpencerTorres))
- Example client: Rename Stream\(\) to Consume\(\) to avoid confusion with RabbitMQ streams [\#39](https://github.com/rabbitmq/amqp091-go/pull/39) ([andygrunwald](https://github.com/andygrunwald))
- Example client: Rename `name` to `queueName` to make the usage clear and explicit [\#38](https://github.com/rabbitmq/amqp091-go/pull/38) ([andygrunwald](https://github.com/andygrunwald))
- Client example: Renamed concept "Session" to "Client" [\#37](https://github.com/rabbitmq/amqp091-go/pull/37) ([andygrunwald](https://github.com/andygrunwald))
- delete unuseful code [\#36](https://github.com/rabbitmq/amqp091-go/pull/36) ([liutaot](https://github.com/liutaot))
- Client Example: Fix closing order [\#35](https://github.com/rabbitmq/amqp091-go/pull/35) ([andygrunwald](https://github.com/andygrunwald))
- Client example: Use instance logger instead of global logger [\#34](https://github.com/rabbitmq/amqp091-go/pull/34) ([andygrunwald](https://github.com/andygrunwald))
## [v1.3.0](https://github.com/rabbitmq/amqp091-go/tree/v1.3.0) (2022-01-13)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.2.0...v1.3.0)
**Closed issues:**
- documentation of changes triggering version updates [\#29](https://github.com/rabbitmq/amqp091-go/issues/29)
- Persistent messages folder [\#27](https://github.com/rabbitmq/amqp091-go/issues/27)
**Merged pull requests:**
- Expose a method to enable out-of-order Publisher Confirms [\#33](https://github.com/rabbitmq/amqp091-go/pull/33) ([benmoss](https://github.com/benmoss))
- Fix Signed 8-bit headers being treated as unsigned [\#26](https://github.com/rabbitmq/amqp091-go/pull/26) ([alex-goodisman](https://github.com/alex-goodisman))
## [v1.2.0](https://github.com/rabbitmq/amqp091-go/tree/v1.2.0) (2021-11-17)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/v1.1.0...v1.2.0)
**Closed issues:**
- No access to this vhost [\#24](https://github.com/rabbitmq/amqp091-go/issues/24)
- copyright issue? [\#12](https://github.com/rabbitmq/amqp091-go/issues/12)
- A possible dead lock when publishing message with confirmation [\#10](https://github.com/rabbitmq/amqp091-go/issues/10)
- Semver release [\#7](https://github.com/rabbitmq/amqp091-go/issues/7)
**Merged pull requests:**
- Fix deadlock between publishing and receiving confirms [\#25](https://github.com/rabbitmq/amqp091-go/pull/25) ([benmoss](https://github.com/benmoss))
- Add GetNextPublishSeqNo for channel in confirm mode [\#23](https://github.com/rabbitmq/amqp091-go/pull/23) ([kamal-github](https://github.com/kamal-github))
- Added support for cert-only login without user and password [\#20](https://github.com/rabbitmq/amqp091-go/pull/20) ([mihaitodor](https://github.com/mihaitodor))
## [v1.1.0](https://github.com/rabbitmq/amqp091-go/tree/v1.1.0) (2021-09-21)
[Full Changelog](https://github.com/rabbitmq/amqp091-go/compare/ebd83429aa8cb06fa569473f623e87675f96d3a9...v1.1.0)
**Closed issues:**
- AMQPLAIN authentication does not work [\#15](https://github.com/rabbitmq/amqp091-go/issues/15)
**Merged pull requests:**
- Fix AMQPLAIN authentication mechanism [\#16](https://github.com/rabbitmq/amqp091-go/pull/16) ([hodbn](https://github.com/hodbn))
- connection: clarify documented behavior of NotifyClose [\#13](https://github.com/rabbitmq/amqp091-go/pull/13) ([pabigot](https://github.com/pabigot))
- Add a link to pkg.go.dev API docs [\#9](https://github.com/rabbitmq/amqp091-go/pull/9) ([benmoss](https://github.com/benmoss))
- add test go version 1.16.x and 1.17.x [\#8](https://github.com/rabbitmq/amqp091-go/pull/8) ([k4n4ry](https://github.com/k4n4ry))
- fix typos [\#6](https://github.com/rabbitmq/amqp091-go/pull/6) ([h44z](https://github.com/h44z))
- Heartbeat interval should be timeout/2 [\#5](https://github.com/rabbitmq/amqp091-go/pull/5) ([ifo20](https://github.com/ifo20))
- Exporting Channel State [\#4](https://github.com/rabbitmq/amqp091-go/pull/4) ([eibrunorodrigues](https://github.com/eibrunorodrigues))
- Add codeql analysis [\#3](https://github.com/rabbitmq/amqp091-go/pull/3) ([MirahImage](https://github.com/MirahImage))
- Add PR github action. [\#2](https://github.com/rabbitmq/amqp091-go/pull/2) ([MirahImage](https://github.com/MirahImage))
- Update Copyright Statement [\#1](https://github.com/rabbitmq/amqp091-go/pull/1) ([rlewis24](https://github.com/rlewis24))
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*

View File

@@ -0,0 +1,77 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in RabbitMQ Operator project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at oss-coc@vmware.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

62
vendor/github.com/rabbitmq/amqp091-go/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,62 @@
# Contributing
## Workflow
Here is the recommended workflow:
1. Fork this repository, **github.com/rabbitmq/amqp091-go**
1. Create your feature branch (`git checkout -b my-new-feature`)
1. Run Static Checks
1. Run integration tests (see below)
1. **Implement tests**
1. Implement fixes
1. Commit your changes. Use a [good, descriptive, commit message][good-commit].
1. Push to a branch (`git push -u origin my-new-feature`)
1. Submit a pull request
[good-commit]: https://cbea.ms/git-commit/
## Running Static Checks
golangci-lint must be installed to run the static checks. See [installation
docs](https://golangci-lint.run/usage/install/) for more information.
The static checks can be run via:
```shell
make checks
```
## Running Tests
### Integration Tests
Running the Integration tests require:
* A running RabbitMQ node with all defaults:
[https://www.rabbitmq.com/download.html](https://www.rabbitmq.com/download.html)
* That the server is either reachable via `amqp://guest:guest@127.0.0.1:5672/`
or the environment variable `AMQP_URL` set to it's URL
(e.g.: `export AMQP_URL="amqp://guest:verysecretpasswd@rabbitmq-host:5772/`)
The integration tests can be run via:
```shell
make tests
```
Some tests require access to `rabbitmqctl` CLI. Use the environment variable
`RABBITMQ_RABBITMQCTL_PATH=/some/path/to/rabbitmqctl` to run those tests.
If you have Docker available in your machine, you can run:
```shell
make tests-docker
```
This target will start a RabbitMQ container, run the test suite with the environment
variable setup, and stop RabbitMQ container after a successful run.
All integration tests should use the `integrationConnection(...)` test
helpers defined in `integration_test.go` to setup the integration environment
and logging.

View File

@@ -1,5 +1,7 @@
Copyright (c) 2012-2019, Sean Treadway, SoundCloud Ltd. AMQP 0-9-1 Go Client
All rights reserved. Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:

57
vendor/github.com/rabbitmq/amqp091-go/Makefile generated vendored Normal file
View File

@@ -0,0 +1,57 @@
.DEFAULT_GOAL := list
# Insert a comment starting with '##' after a target, and it will be printed by 'make' and 'make list'
.PHONY: list
list: ## list Makefile targets
@echo "The most used targets: \n"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
.PHONY: check-fmt
check-fmt: ## Ensure code is formatted
gofmt -l -d . # For the sake of debugging
test -z "$$(gofmt -l .)"
.PHONY: fmt
fmt: ## Run go fmt against code
go fmt ./...
.PHONY: tests
tests: ## Run all tests and requires a running rabbitmq-server. Use GO_TEST_FLAGS to add extra flags to go test
go test -race -v -tags integration $(GO_TEST_FLAGS)
.PHONY: tests-docker
tests-docker: rabbitmq-server
RABBITMQ_RABBITMQCTL_PATH="DOCKER:$(CONTAINER_NAME)" go test -race -v -tags integration $(GO_TEST_FLAGS)
$(MAKE) stop-rabbitmq-server
.PHONY: check
check:
golangci-lint run ./...
CONTAINER_NAME ?= amqp091-go-rabbitmq
.PHONY: rabbitmq-server
rabbitmq-server: ## Start a RabbitMQ server using Docker. Container name can be customised with CONTAINER_NAME=some-rabbit
docker run --detach --rm --name $(CONTAINER_NAME) \
--publish 5672:5672 --publish 15672:15672 \
--pull always rabbitmq:3-management
.PHONY: stop-rabbitmq-server
stop-rabbitmq-server: ## Stop a RabbitMQ server using Docker. Container name can be customised with CONTAINER_NAME=some-rabbit
docker stop $(CONTAINER_NAME)
certs:
./certs.sh
.PHONY: certs-rm
certs-rm:
rm -r ./certs/
.PHONY: rabbitmq-server-tls
rabbitmq-server-tls: | certs ## Start a RabbitMQ server using Docker. Container name can be customised with CONTAINER_NAME=some-rabbit
docker run --detach --rm --name $(CONTAINER_NAME) \
--publish 5672:5672 --publish 5671:5671 --publish 15672:15672 \
--mount type=bind,src=./certs/server,dst=/certs \
--mount type=bind,src=./certs/ca/cacert.pem,dst=/certs/cacert.pem,readonly \
--mount type=bind,src=./rabbitmq-confs/tls/90-tls.conf,dst=/etc/rabbitmq/conf.d/90-tls.conf \
--pull always rabbitmq:3-management

105
vendor/github.com/rabbitmq/amqp091-go/README.md generated vendored Normal file
View File

@@ -0,0 +1,105 @@
# Go RabbitMQ Client Library
[![amqp091-go](https://github.com/rabbitmq/amqp091-go/actions/workflows/tests.yml/badge.svg)](https://github.com/rabbitmq/amqp091-go/actions/workflows/tests.yml)
[![Go Reference](https://pkg.go.dev/badge/github.com/rabbitmq/amqp091-go.svg)](https://pkg.go.dev/github.com/rabbitmq/amqp091-go)
[![Go Report Card](https://goreportcard.com/badge/github.com/rabbitmq/amqp091-go)](https://goreportcard.com/report/github.com/rabbitmq/amqp091-go)
This is a Go AMQP 0.9.1 client maintained by the [RabbitMQ core team](https://github.com/rabbitmq).
It was [originally developed by Sean Treadway](https://github.com/streadway/amqp).
## Differences from streadway/amqp
Some things are different compared to the original client,
others haven't changed.
### Package Name
This library uses a different package name. If moving from `streadway/amqp`,
using an alias may reduce the number of changes needed:
``` go
amqp "github.com/rabbitmq/amqp091-go"
```
### License
This client uses the same 2-clause BSD license as the original project.
### Public API Evolution
This client retains key API elements as practically possible.
It is, however, open to reasonable breaking public API changes suggested by the community.
We don't have the "no breaking public API changes ever" rule and fully recognize
that a good client API evolves over time.
## Project Maturity
This project is based on a mature Go client that's been around for over a decade.
## Supported Go Versions
This client supports two most recent Go release series.
## Supported RabbitMQ Versions
This project supports RabbitMQ versions starting with `2.0` but primarily tested
against [currently supported RabbitMQ release series](https://www.rabbitmq.com/versions.html).
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. AMQP 1.0 is a divergent
specification (a different protocol) and belongs to a different library.
* 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.
* Support for [`basic.return` and `basic.ack` frame ordering](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed).
This client uses Go channels for certain protocol events and ordering between
events sent to two different channels generally cannot be guaranteed.
## Usage
See the [_examples](_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
* [Godoc API reference](http://godoc.org/github.com/rabbitmq/amqp091-go)
* [RabbitMQ tutorials in Go](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go)
## Contributing
Pull requests are very much welcomed. Create your pull request on a non-main
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.
See [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
## License
BSD 2 clause, see LICENSE for more details.

16
vendor/github.com/rabbitmq/amqp091-go/RELEASE.md generated vendored Normal file
View File

@@ -0,0 +1,16 @@
# Guide to release a new version
1. Update the `buildVersion` constant in [connection.go](https://github.com/rabbitmq/amqp091-go/blob/4886c35d10b273bd374e3ed2356144ad41d27940/connection.go#L31)
2. Commit and push. Include the version in the commit message e.g. [this commit](https://github.com/rabbitmq/amqp091-go/commit/52ce2efd03c53dcf77d5496977da46840e9abd24)
3. Create a new [GitHub Release](https://github.com/rabbitmq/amqp091-go/releases). Create a new tag as `v<MAJOR>.<MINOR>.<PATCH>`
1. Use auto-generate release notes feature in GitHub
4. Generate the change log, see [Changelog Generation](#changelog-generation)
5. Review the changelog. Watch out for issues closed as "not-fixed" or without a PR
6. Commit and Push. Pro-tip: include `[skip ci]` in the commit message to skip the CI run, since it's only documentation
7. Send an announcement to the mailing list. Take inspiration from [this message](https://groups.google.com/g/rabbitmq-users/c/EBGYGOWiSgs/m/0sSFuAGICwAJ)
## Changelog Generation
```
github_changelog_generator --token GITHUB-TOKEN -u rabbitmq -p amqp091-go --no-unreleased --release-branch main
```

View File

@@ -1,4 +1,9 @@
package amqp // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package amqp091
import ( import (
"bytes" "bytes"
@@ -13,10 +18,10 @@ const (
// allocator maintains a bitset of allocated numbers. // allocator maintains a bitset of allocated numbers.
type allocator struct { type allocator struct {
pool *big.Int pool *big.Int
last int follow int
low int low int
high int high int
} }
// NewAllocator reserves and frees integers out of a range between low and // NewAllocator reserves and frees integers out of a range between low and
@@ -26,10 +31,10 @@ type allocator struct {
// sizeof(big.Word) // sizeof(big.Word)
func newAllocator(low, high int) *allocator { func newAllocator(low, high int) *allocator {
return &allocator{ return &allocator{
pool: big.NewInt(0), pool: big.NewInt(0),
last: low, follow: low,
low: low, low: low,
high: high, high: high,
} }
} }
@@ -64,21 +69,29 @@ func (a allocator) String() string {
// O(N) worst case runtime where N is allocated, but usually O(1) due to a // O(N) worst case runtime where N is allocated, but usually O(1) due to a
// rolling index into the oldest allocation. // rolling index into the oldest allocation.
func (a *allocator) next() (int, bool) { func (a *allocator) next() (int, bool) {
wrapped := a.last wrapped := a.follow
defer func() {
// make a.follow point to next value
if a.follow == a.high {
a.follow = a.low
} else {
a.follow += 1
}
}()
// Find trailing bit // Find trailing bit
for ; a.last <= a.high; a.last++ { for ; a.follow <= a.high; a.follow++ {
if a.reserve(a.last) { if a.reserve(a.follow) {
return a.last, true return a.follow, true
} }
} }
// Find preceding free'd pool // Find preceding free'd pool
a.last = a.low a.follow = a.low
for ; a.last < wrapped; a.last++ { for ; a.follow < wrapped; a.follow++ {
if a.reserve(a.last) { if a.reserve(a.follow) {
return a.last, true return a.follow, true
} }
} }

View File

@@ -1,11 +1,12 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"bytes"
"fmt" "fmt"
) )
@@ -43,13 +44,33 @@ func (auth *AMQPlainAuth) Mechanism() string {
return "AMQPLAIN" return "AMQPLAIN"
} }
// Response returns the null character delimited encoding for the SASL PLAIN Mechanism. // Response returns an AMQP encoded credentials table, without the field table size.
func (auth *AMQPlainAuth) Response() string { func (auth *AMQPlainAuth) Response() string {
return fmt.Sprintf("LOGIN:%sPASSWORD:%s", auth.Username, auth.Password) var buf bytes.Buffer
table := Table{"LOGIN": auth.Username, "PASSWORD": auth.Password}
if err := writeTable(&buf, table); err != nil {
return ""
}
return buf.String()[4:]
}
// ExternalAuth for RabbitMQ-auth-mechanism-ssl.
type ExternalAuth struct {
}
// Mechanism returns "EXTERNAL"
func (*ExternalAuth) Mechanism() string {
return "EXTERNAL"
}
// Response returns an AMQP encoded credentials table, without the field table size.
func (*ExternalAuth) Response() string {
return "\000*\000*"
} }
// Finds the first mechanism preferred by the client that the server supports. // Finds the first mechanism preferred by the client that the server supports.
func pickSASLMechanism(client []Authentication, serverMechanisms []string) (auth Authentication, ok bool) { func pickSASLMechanism(client []Authentication, serverMechanisms []string) (auth Authentication, ok bool) {
for _, auth = range client { for _, auth = range client {
for _, mech := range serverMechanisms { for _, mech := range serverMechanisms {
if auth.Mechanism() == mech { if auth.Mechanism() == mech {

View File

@@ -38,7 +38,7 @@ serial = $dir/serial
default_crl_days = 7 default_crl_days = 7
default_days = 3650 default_days = 3650
default_md = sha1 default_md = sha256
policy = testca_policy policy = testca_policy
x509_extensions = certificate_extensions x509_extensions = certificate_extensions
@@ -57,7 +57,7 @@ basicConstraints = CA:false
[ req ] [ req ]
default_bits = 2048 default_bits = 2048
default_keyfile = ./private/cakey.pem default_keyfile = ./private/cakey.pem
default_md = sha1 default_md = sha256
prompt = yes prompt = yes
distinguished_name = root_ca_distinguished_name distinguished_name = root_ca_distinguished_name
x509_extensions = root_ca_extensions x509_extensions = root_ca_extensions
@@ -71,12 +71,12 @@ keyUsage = keyCertSign, cRLSign
[ client_ca_extensions ] [ client_ca_extensions ]
basicConstraints = CA:false basicConstraints = CA:false
keyUsage = digitalSignature keyUsage = keyEncipherment,digitalSignature
extendedKeyUsage = 1.3.6.1.5.5.7.3.2 extendedKeyUsage = 1.3.6.1.5.5.7.3.2
[ server_ca_extensions ] [ server_ca_extensions ]
basicConstraints = CA:false basicConstraints = CA:false
keyUsage = keyEncipherment keyUsage = keyEncipherment,digitalSignature
extendedKeyUsage = 1.3.6.1.5.5.7.3.1 extendedKeyUsage = 1.3.6.1.5.5.7.3.1
subjectAltName = @alt_names subjectAltName = @alt_names
@@ -106,7 +106,7 @@ openssl req \
-new \ -new \
-nodes \ -nodes \
-config openssl.cnf \ -config openssl.cnf \
-subj "/CN=127.0.0.1/O=server/" \ -subj "/CN=localhost/O=server/" \
-key $root/server/key.pem \ -key $root/server/key.pem \
-out $root/server/req.pem \ -out $root/server/req.pem \
-outform PEM -outform PEM
@@ -115,7 +115,7 @@ openssl req \
-new \ -new \
-nodes \ -nodes \
-config openssl.cnf \ -config openssl.cnf \
-subj "/CN=127.0.0.1/O=client/" \ -subj "/CN=localhost/O=client/" \
-key $root/client/key.pem \ -key $root/client/key.pem \
-out $root/client/req.pem \ -out $root/client/req.pem \
-outform PEM -outform PEM

View File

@@ -0,0 +1,4 @@
#!/bin/bash
echo $1 > VERSION
sed -i -e "s/.*buildVersion = \"*.*/buildVersion = \"$1\"/" ./connection.go
go fmt ./...

View File

@@ -1,11 +1,12 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"context"
"reflect" "reflect"
"sync" "sync"
"sync/atomic" "sync/atomic"
@@ -15,14 +16,14 @@ import (
// +------+---------+-------------+ +------------+ +-----------+ // +------+---------+-------------+ +------------+ +-----------+
// | type | channel | size | | payload | | frame-end | // | type | channel | size | | payload | | frame-end |
// +------+---------+-------------+ +------------+ +-----------+ // +------+---------+-------------+ +------------+ +-----------+
// octet short long size octets octet //
// octet short long size octets octet
const frameHeaderSize = 1 + 2 + 4 + 1 const frameHeaderSize = 1 + 2 + 4 + 1
/* /*
Channel represents an AMQP channel. Used as a context for valid message 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 exchange. Errors on methods with this Channel as a receiver means this channel
should be discarded and a new channel established. should be discarded and a new channel established.
*/ */
type Channel struct { type Channel struct {
destructor sync.Once destructor sync.Once
@@ -39,6 +40,7 @@ type Channel struct {
// closed is set to 1 when the channel has been closed - see Channel.send() // closed is set to 1 when the channel has been closed - see Channel.send()
closed int32 closed int32
close chan struct{}
// true when we will never notify again // true when we will never notify again
noNotify bool noNotify bool
@@ -66,7 +68,7 @@ type Channel struct {
errors chan *Error errors chan *Error
// State machine that manages frame order, must only be mutated by the connection // State machine that manages frame order, must only be mutated by the connection
recv func(*Channel, frame) error recv func(*Channel, frame)
// Current state for frame re-assembly, only mutated from recv // Current state for frame re-assembly, only mutated from recv
message messageWithContent message messageWithContent
@@ -84,12 +86,20 @@ func newChannel(c *Connection, id uint16) *Channel {
confirms: newConfirms(), confirms: newConfirms(),
recv: (*Channel).recvMethod, recv: (*Channel).recvMethod,
errors: make(chan *Error, 1), errors: make(chan *Error, 1),
close: make(chan struct{}),
} }
} }
// Signal that from now on, Channel.send() should call Channel.sendClosed()
func (ch *Channel) setClosed() {
atomic.StoreInt32(&ch.closed, 1)
}
// shutdown is called by Connection after the channel has been removed from the // shutdown is called by Connection after the channel has been removed from the
// connection registry. // connection registry.
func (ch *Channel) shutdown(e *Error) { func (ch *Channel) shutdown(e *Error) {
ch.setClosed()
ch.destructor.Do(func() { ch.destructor.Do(func() {
ch.m.Lock() ch.m.Lock()
defer ch.m.Unlock() defer ch.m.Unlock()
@@ -103,14 +113,7 @@ func (ch *Channel) shutdown(e *Error) {
for _, c := range ch.closes { for _, c := range ch.closes {
c <- e c <- e
} }
} // Notify RPC if we're selecting
// Signal that from now on, Channel.send() should call
// Channel.sendClosed()
atomic.StoreInt32(&ch.closed, 1)
// Notify RPC if we're selecting
if e != nil {
ch.errors <- e ch.errors <- e
} }
@@ -144,6 +147,7 @@ func (ch *Channel) shutdown(e *Error) {
} }
close(ch.errors) close(ch.errors)
close(ch.close)
ch.noNotify = true ch.noNotify = true
}) })
} }
@@ -154,7 +158,7 @@ func (ch *Channel) shutdown(e *Error) {
// only 'channel.close' is sent to the server. // only 'channel.close' is sent to the server.
func (ch *Channel) send(msg message) (err error) { func (ch *Channel) send(msg message) (err error) {
// If the channel is closed, use Channel.sendClosed() // If the channel is closed, use Channel.sendClosed()
if atomic.LoadInt32(&ch.closed) == 1 { if ch.IsClosed() {
return ch.sendClosed(msg) return ch.sendClosed(msg)
} }
@@ -230,14 +234,38 @@ func (ch *Channel) sendOpen(msg message) (err error) {
size = len(body) size = len(body)
} }
if err = ch.connection.send(&methodFrame{ // If the channel is closed, use Channel.sendClosed()
if ch.IsClosed() {
return ch.sendClosed(msg)
}
// Flush the buffer only after all the Frames that comprise the Message
// have been written to maximise benefits of using a buffered writer.
defer func() {
if endError := ch.connection.endSendUnflushed(); endError != nil {
if err == nil {
err = endError
}
}
}()
// We use sendUnflushed() in this method as sending the message requires
// sending multiple Frames (methodFrame, headerFrame, N x bodyFrame).
// Flushing after each Frame is inefficient, as it negates much of the
// benefit of using a buffered writer and results in more syscalls than
// necessary. Flushing buffers after every frame can have a significant
// performance impact when sending (e.g. basicPublish) small messages,
// so sendUnflushed() performs an *Unflushed* write, but is otherwise
// equivalent to the send() method. We later use the separate flush
// method to explicitly flush the buffer after all Frames are written.
if err = ch.connection.sendUnflushed(&methodFrame{
ChannelId: ch.id, ChannelId: ch.id,
Method: content, Method: content,
}); err != nil { }); err != nil {
return return
} }
if err = ch.connection.send(&headerFrame{ if err = ch.connection.sendUnflushed(&headerFrame{
ChannelId: ch.id, ChannelId: ch.id,
ClassId: class, ClassId: class,
Size: uint64(len(body)), Size: uint64(len(body)),
@@ -252,7 +280,7 @@ func (ch *Channel) sendOpen(msg message) (err error) {
j = len(body) j = len(body)
} }
if err = ch.connection.send(&bodyFrame{ if err = ch.connection.sendUnflushed(&bodyFrame{
ChannelId: ch.id, ChannelId: ch.id,
Body: body[i:j], Body: body[i:j],
}); err != nil { }); err != nil {
@@ -260,6 +288,11 @@ func (ch *Channel) sendOpen(msg message) (err error) {
} }
} }
} else { } else {
// If the channel is closed, use Channel.sendClosed()
if ch.IsClosed() {
return ch.sendClosed(msg)
}
err = ch.connection.send(&methodFrame{ err = ch.connection.send(&methodFrame{
ChannelId: ch.id, ChannelId: ch.id,
Method: msg, Method: msg,
@@ -274,11 +307,16 @@ func (ch *Channel) sendOpen(msg message) (err error) {
func (ch *Channel) dispatch(msg message) { func (ch *Channel) dispatch(msg message) {
switch m := msg.(type) { switch m := msg.(type) {
case *channelClose: case *channelClose:
// Note: channel state is set to closed immedately after the message is
// decoded by the Connection
// lock before sending connection.close-ok // lock before sending connection.close-ok
// to avoid unexpected interleaving with basic.publish frames if // to avoid unexpected interleaving with basic.publish frames if
// publishing is happening concurrently // publishing is happening concurrently
ch.m.Lock() ch.m.Lock()
ch.send(&channelCloseOk{}) if err := ch.send(&channelCloseOk{}); err != nil {
Logger.Printf("error sending channelCloseOk, channel id: %d error: %+v", ch.id, err)
}
ch.m.Unlock() ch.m.Unlock()
ch.connection.closeChannel(ch, newError(m.ReplyCode, m.ReplyText)) ch.connection.closeChannel(ch, newError(m.ReplyCode, m.ReplyText))
@@ -288,7 +326,9 @@ func (ch *Channel) dispatch(msg message) {
c <- m.Active c <- m.Active
} }
ch.notifyM.RUnlock() ch.notifyM.RUnlock()
ch.send(&channelFlowOk{Active: m.Active}) if err := ch.send(&channelFlowOk{Active: m.Active}); err != nil {
Logger.Printf("error sending channelFlowOk, channel id: %d error: %+v", ch.id, err)
}
case *basicCancel: case *basicCancel:
ch.notifyM.RLock() ch.notifyM.RLock()
@@ -330,44 +370,49 @@ func (ch *Channel) dispatch(msg message) {
// deliveries are in flight and a no-wait cancel has happened // deliveries are in flight and a no-wait cancel has happened
default: default:
ch.rpc <- msg select {
case <-ch.close:
return
case ch.rpc <- msg:
}
} }
} }
func (ch *Channel) transition(f func(*Channel, frame) error) error { func (ch *Channel) transition(f func(*Channel, frame)) {
ch.recv = f ch.recv = f
return nil
} }
func (ch *Channel) recvMethod(f frame) error { func (ch *Channel) recvMethod(f frame) {
switch frame := f.(type) { switch frame := f.(type) {
case *methodFrame: case *methodFrame:
if msg, ok := frame.Method.(messageWithContent); ok { if msg, ok := frame.Method.(messageWithContent); ok {
ch.body = make([]byte, 0) ch.body = make([]byte, 0)
ch.message = msg ch.message = msg
return ch.transition((*Channel).recvHeader) ch.transition((*Channel).recvHeader)
return
} }
ch.dispatch(frame.Method) // termination state ch.dispatch(frame.Method) // termination state
return ch.transition((*Channel).recvMethod) ch.transition((*Channel).recvMethod)
case *headerFrame: case *headerFrame:
// drop // drop
return ch.transition((*Channel).recvMethod) ch.transition((*Channel).recvMethod)
case *bodyFrame: case *bodyFrame:
// drop // drop
return ch.transition((*Channel).recvMethod) ch.transition((*Channel).recvMethod)
}
panic("unexpected frame type") default:
panic("unexpected frame type")
}
} }
func (ch *Channel) recvHeader(f frame) error { func (ch *Channel) recvHeader(f frame) {
switch frame := f.(type) { switch frame := f.(type) {
case *methodFrame: case *methodFrame:
// interrupt content and handle method // interrupt content and handle method
return ch.recvMethod(f) ch.recvMethod(f)
case *headerFrame: case *headerFrame:
// start collecting if we expect body frames // start collecting if we expect body frames
@@ -376,29 +421,31 @@ func (ch *Channel) recvHeader(f frame) error {
if frame.Size == 0 { if frame.Size == 0 {
ch.message.setContent(ch.header.Properties, ch.body) ch.message.setContent(ch.header.Properties, ch.body)
ch.dispatch(ch.message) // termination state ch.dispatch(ch.message) // termination state
return ch.transition((*Channel).recvMethod) ch.transition((*Channel).recvMethod)
return
} }
return ch.transition((*Channel).recvContent) ch.transition((*Channel).recvContent)
case *bodyFrame: case *bodyFrame:
// drop and reset // drop and reset
return ch.transition((*Channel).recvMethod) ch.transition((*Channel).recvMethod)
}
panic("unexpected frame type") default:
panic("unexpected frame type")
}
} }
// state after method + header and before the length // state after method + header and before the length
// defined by the header has been reached // defined by the header has been reached
func (ch *Channel) recvContent(f frame) error { func (ch *Channel) recvContent(f frame) {
switch frame := f.(type) { switch frame := f.(type) {
case *methodFrame: case *methodFrame:
// interrupt content and handle method // interrupt content and handle method
return ch.recvMethod(f) ch.recvMethod(f)
case *headerFrame: case *headerFrame:
// drop and reset // drop and reset
return ch.transition((*Channel).recvMethod) ch.transition((*Channel).recvMethod)
case *bodyFrame: case *bodyFrame:
if cap(ch.body) == 0 { if cap(ch.body) == 0 {
@@ -409,13 +456,15 @@ func (ch *Channel) recvContent(f frame) error {
if uint64(len(ch.body)) >= ch.header.Size { if uint64(len(ch.body)) >= ch.header.Size {
ch.message.setContent(ch.header.Properties, ch.body) ch.message.setContent(ch.header.Properties, ch.body)
ch.dispatch(ch.message) // termination state ch.dispatch(ch.message) // termination state
return ch.transition((*Channel).recvMethod) ch.transition((*Channel).recvMethod)
return
} }
return ch.transition((*Channel).recvContent) ch.transition((*Channel).recvContent)
}
panic("unexpected frame type") default:
panic("unexpected frame type")
}
} }
/* /*
@@ -423,9 +472,12 @@ Close initiate a clean channel closure by sending a close message with the error
code set to '200'. code set to '200'.
It is safe to call this method multiple times. It is safe to call this method multiple times.
*/ */
func (ch *Channel) Close() error { func (ch *Channel) Close() error {
if ch.IsClosed() {
return nil
}
defer ch.connection.closeChannel(ch, nil) defer ch.connection.closeChannel(ch, nil)
return ch.call( return ch.call(
&channelClose{ReplyCode: replySuccess}, &channelClose{ReplyCode: replySuccess},
@@ -433,6 +485,12 @@ func (ch *Channel) Close() error {
) )
} }
// IsClosed returns true if the channel is marked as closed, otherwise false
// is returned.
func (ch *Channel) IsClosed() bool {
return atomic.LoadInt32(&ch.closed) == 1
}
/* /*
NotifyClose registers a listener for when the server sends a channel or 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 exception in the form of a Connection.Close or Channel.Close method.
@@ -443,6 +501,8 @@ this channel.
The chan provided will be closed when the Channel is closed and on a The chan provided will be closed when the Channel is closed and on a
graceful close, no error will be sent. graceful close, no error will be sent.
In case of a non graceful close the error will be notified synchronously by the library
so that it will be necessary to consume the Channel from the caller in order to avoid deadlocks
*/ */
func (ch *Channel) NotifyClose(c chan *Error) chan *Error { func (ch *Channel) NotifyClose(c chan *Error) chan *Error {
ch.notifyM.Lock() ch.notifyM.Lock()
@@ -488,7 +548,6 @@ much on the same connection, all channels using that connection will suffer,
including acknowledgments from deliveries. Use different Connections if you including acknowledgments from deliveries. Use different Connections if you
desire to interleave consumers and producers in the same process to avoid your 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. basic.ack messages from getting rate limited with your basic.publish messages.
*/ */
func (ch *Channel) NotifyFlow(c chan bool) chan bool { func (ch *Channel) NotifyFlow(c chan bool) chan bool {
ch.notifyM.Lock() ch.notifyM.Lock()
@@ -510,7 +569,6 @@ immediate flags.
A return struct has a copy of the Publishing along with some error A return struct has a copy of the Publishing along with some error
information about why the publishing failed. information about why the publishing failed.
*/ */
func (ch *Channel) NotifyReturn(c chan Return) chan Return { func (ch *Channel) NotifyReturn(c chan Return) chan Return {
ch.notifyM.Lock() ch.notifyM.Lock()
@@ -531,7 +589,6 @@ 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). where the master has just failed (and was moved to another node).
The subscription tag is returned to the listener. The subscription tag is returned to the listener.
*/ */
func (ch *Channel) NotifyCancel(c chan string) chan string { func (ch *Channel) NotifyCancel(c chan string) chan string {
ch.notifyM.Lock() ch.notifyM.Lock()
@@ -594,6 +651,8 @@ or Channel while confirms are in-flight.
It's advisable to wait for all Confirmations to arrive before calling It's advisable to wait for all Confirmations to arrive before calling
Channel.Close() or Connection.Close(). Channel.Close() or Connection.Close().
It is also advisable for the caller to consume from the channel returned till it is closed
to avoid possible deadlocks
*/ */
func (ch *Channel) NotifyPublish(confirm chan Confirmation) chan Confirmation { func (ch *Channel) NotifyPublish(confirm chan Confirmation) chan Confirmation {
ch.notifyM.Lock() ch.notifyM.Lock()
@@ -606,7 +665,6 @@ func (ch *Channel) NotifyPublish(confirm chan Confirmation) chan Confirmation {
} }
return confirm return confirm
} }
/* /*
@@ -672,7 +730,6 @@ 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 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 require an acknowledgment, otherwise they will arrive and be dropped in the
client without an ack, and will not be redelivered to other consumers. client without an ack, and will not be redelivered to other consumers.
*/ */
func (ch *Channel) Cancel(consumer string, noWait bool) error { func (ch *Channel) Cancel(consumer string, noWait bool) error {
req := &basicCancel{ req := &basicCancel{
@@ -705,12 +762,12 @@ 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 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. this queue by publishing to "" with the routing key of the queue name.
QueueDeclare("alerts", true, false, false, false, nil) QueueDeclare("alerts", true, false, false, false, nil)
Publish("", "alerts", false, false, Publishing{Body: []byte("...")}) Publish("", "alerts", false, false, Publishing{Body: []byte("...")})
Delivery Exchange Key Queue Delivery Exchange Key Queue
----------------------------------------------- -----------------------------------------------
key: alerts -> "" -> alerts -> alerts key: alerts -> "" -> alerts -> alerts
The queue name may be empty, in which case the server will generate a unique name 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. which will be returned in the Name field of Queue struct.
@@ -746,7 +803,6 @@ 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 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. 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) { func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
if err := args.Validate(); err != nil { if err := args.Validate(); err != nil {
@@ -780,13 +836,11 @@ func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noW
} }
/* /*
QueueDeclarePassive is functionally and parametrically equivalent to QueueDeclarePassive is functionally and parametrically equivalent to
QueueDeclare, except that it sets the "passive" attribute to true. A passive 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 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 non-existent queue will cause RabbitMQ to throw an exception. This function
can be used to test for the existence of a queue. 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) { func (ch *Channel) QueueDeclarePassive(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
if err := args.Validate(); err != nil { if err := args.Validate(); err != nil {
@@ -833,6 +887,7 @@ declared with specific parameters.
If a queue by this name does not exist, an error will be returned and the If a queue by this name does not exist, an error will be returned and the
channel will be closed. channel will be closed.
Deprecated: Use QueueDeclare with "Passive: true" instead.
*/ */
func (ch *Channel) QueueInspect(name string) (Queue, error) { func (ch *Channel) QueueInspect(name string) (Queue, error) {
req := &queueDeclare{ req := &queueDeclare{
@@ -857,14 +912,14 @@ 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 be routed to the queue when the publishing routing key matches the binding
routing key. routing key.
QueueBind("pagers", "alert", "log", false, nil) QueueBind("pagers", "alert", "log", false, nil)
QueueBind("emails", "info", "log", false, nil) QueueBind("emails", "info", "log", false, nil)
Delivery Exchange Key Queue Delivery Exchange Key Queue
----------------------------------------------- -----------------------------------------------
key: alert --> log ----> alert --> pagers key: alert --> log ----> alert --> pagers
key: info ---> log ----> info ---> emails key: info ---> log ----> info ---> emails
key: debug --> log (none) (dropped) key: debug --> log (none) (dropped)
If a binding with the same key and arguments already exists between the 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 exchange and queue, the attempt to rebind will be ignored and the existing
@@ -874,16 +929,16 @@ 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 same queue, the server will only route the publishing once. This is possible
with topic exchanges. with topic exchanges.
QueueBind("pagers", "alert", "amq.topic", false, nil) QueueBind("pagers", "alert", "amq.topic", false, nil)
QueueBind("emails", "info", "amq.topic", false, nil) QueueBind("emails", "info", "amq.topic", false, nil)
QueueBind("emails", "#", "amq.topic", false, nil) // match everything QueueBind("emails", "#", "amq.topic", false, nil) // match everything
Delivery Exchange Key Queue Delivery Exchange Key Queue
----------------------------------------------- -----------------------------------------------
key: alert --> amq.topic ----> alert --> pagers key: alert --> amq.topic ----> alert --> pagers
key: info ---> amq.topic ----> # ------> emails key: info ---> amq.topic ----> # ------> emails
\---> info ---/ \---> info ---/
key: debug --> amq.topic ----> # ------> emails key: debug --> amq.topic ----> # ------> emails
It is only possible to bind a durable queue to a durable exchange regardless of 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 whether the queue or exchange is auto-deleted. Bindings between durable queues
@@ -894,7 +949,6 @@ will be closed.
When noWait is false and the queue could not be bound, the channel will be When noWait is false and the queue could not be bound, the channel will be
closed with an error. closed with an error.
*/ */
func (ch *Channel) QueueBind(name, key, exchange string, noWait bool, args Table) error { func (ch *Channel) QueueBind(name, key, exchange string, noWait bool, args Table) error {
if err := args.Validate(); err != nil { if err := args.Validate(); err != nil {
@@ -916,10 +970,6 @@ func (ch *Channel) QueueBind(name, key, exchange string, noWait bool, args Table
/* /*
QueueUnbind removes a binding between an exchange and queue matching the key and QueueUnbind removes a binding between an exchange and queue matching the key and
arguments. 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 { func (ch *Channel) QueueUnbind(name, key, exchange string, args Table) error {
if err := args.Validate(); err != nil { if err := args.Validate(); err != nil {
@@ -976,7 +1026,6 @@ 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 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 could not be deleted, a channel exception will be raised and the channel will
be closed. be closed.
*/ */
func (ch *Channel) QueueDelete(name string, ifUnused, ifEmpty, noWait bool) (int, error) { func (ch *Channel) QueueDelete(name string, ifUnused, ifEmpty, noWait bool) (int, error) {
req := &queueDelete{ req := &queueDelete{
@@ -1043,11 +1092,11 @@ Inflight messages, limited by Channel.Qos will be buffered until received from
the returned chan. the returned chan.
When the Channel or Connection is closed, all buffered and inflight messages will When the Channel or Connection is closed, all buffered and inflight messages will
be dropped. be dropped. RabbitMQ will requeue messages not acknowledged. In other words, dropped
messages in this way won't be lost.
When the consumer tag is cancelled, all inflight messages will be delivered until When the consumer tag is cancelled, all inflight messages will be delivered until
the returned chan is closed. the returned chan is closed.
*/ */
func (ch *Channel) Consume(queue, consumer string, autoAck, exclusive, noLocal, noWait bool, args Table) (<-chan Delivery, error) { 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 // When we return from ch.call, there may be a delivery already for the
@@ -1082,7 +1131,122 @@ func (ch *Channel) Consume(queue, consumer string, autoAck, exclusive, noLocal,
return nil, err return nil, err
} }
return (<-chan Delivery)(deliveries), nil return deliveries, nil
}
/*
ConsumeWithContext immediately starts delivering queued messages.
This function is similar to Channel.Consume, and accepts a context to control
consumer lifecycle. When the context passed to this function is canceled, the
consumer associated with the deliveries channel will be canceled too. When the
context passed to this function is cancelled, the deliveries channel will be closed.
An application is advised to keep on receiving messages from the delivery channel
until the channel is empty. This is specially important to avoid memory leaks from
unconsumed messages from the delivery channel.
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, context is cancelled, 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.
See http://www.rabbitmq.com/confirms.html for more details.
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.
The noLocal flag is not supported by RabbitMQ.
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.
Inflight messages, limited by Channel.Qos will be buffered until received from
the returned chan.
When the Channel or Connection is closed, all buffered and inflight messages will
be dropped. RabbitMQ will requeue messages not acknowledged. In other words, dropped
messages in this way won't be lost.
*/
func (ch *Channel) ConsumeWithContext(ctx context.Context, 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{}
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
deliveries := make(chan Delivery)
ch.consumers.add(consumer, deliveries)
if err := ch.call(req, res); err != nil {
ch.consumers.cancel(consumer)
return nil, err
}
go func() {
select {
case <-ch.consumers.closed:
return
case <-ctx.Done():
if ch != nil {
_ = ch.Cancel(consumer, false)
}
}
}()
return deliveries, nil
} }
/* /*
@@ -1126,7 +1290,7 @@ 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, so queues that bind to these pre-declared exchanges must also be
durable. durable.
Exchanges declared as `internal` do not accept accept publishings. Internal Exchanges declared as `internal` do not accept publishings. Internal
exchanges are useful when you wish to implement inter-exchange topologies exchanges are useful when you wish to implement inter-exchange topologies
that should not be exposed to users of the broker. that should not be exposed to users of the broker.
@@ -1158,13 +1322,11 @@ func (ch *Channel) ExchangeDeclare(name, kind string, durable, autoDelete, inter
} }
/* /*
ExchangeDeclarePassive is functionally and parametrically equivalent to ExchangeDeclarePassive is functionally and parametrically equivalent to
ExchangeDeclare, except that it sets the "passive" attribute to true. A passive 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 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 non-existent exchange will cause RabbitMQ to throw an exception. This function
can be used to detect the existence of an exchange. 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 { func (ch *Channel) ExchangeDeclarePassive(name, kind string, durable, autoDelete, internal, noWait bool, args Table) error {
if err := args.Validate(); err != nil { if err := args.Validate(); err != nil {
@@ -1227,14 +1389,14 @@ exchange even though multiple bindings will match.
Given a message delivered to the source exchange, the message will be forwarded Given a message delivered to the source exchange, the message will be forwarded
to the destination exchange when the routing key is matched. to the destination exchange when the routing key is matched.
ExchangeBind("sell", "MSFT", "trade", false, nil) ExchangeBind("sell", "MSFT", "trade", false, nil)
ExchangeBind("buy", "AAPL", "trade", false, nil) ExchangeBind("buy", "AAPL", "trade", false, nil)
Delivery Source Key Destination Delivery Source Key Destination
example exchange exchange example exchange exchange
----------------------------------------------- -----------------------------------------------
key: AAPL --> trade ----> MSFT sell key: AAPL --> trade ----> MSFT sell
\---> AAPL --> buy \---> AAPL --> buy
When noWait is true, do not wait for the server to confirm the binding. If any 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 error occurs the channel will be closed. Add a listener to NotifyClose to
@@ -1321,16 +1483,69 @@ 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 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. internal counter for DeliveryTags with the first confirmation starts at 1.
*/ */
func (ch *Channel) Publish(exchange, key string, mandatory, immediate bool, msg Publishing) error { func (ch *Channel) Publish(exchange, key string, mandatory, immediate bool, msg Publishing) error {
_, err := ch.PublishWithDeferredConfirm(exchange, key, mandatory, immediate, msg)
return err
}
/*
PublishWithContext sends a Publishing from the client to an exchange on the server.
NOTE: this function is equivalent to [Channel.Publish]. Context is not honoured.
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) PublishWithContext(_ context.Context, exchange, key string, mandatory, immediate bool, msg Publishing) error {
return ch.Publish(exchange, key, mandatory, immediate, msg)
}
/*
PublishWithDeferredConfirm behaves identically to Publish, but additionally
returns a DeferredConfirmation, allowing the caller to wait on the publisher
confirmation for this message. If the channel has not been put into confirm
mode, the DeferredConfirmation will be nil.
*/
func (ch *Channel) PublishWithDeferredConfirm(exchange, key string, mandatory, immediate bool, msg Publishing) (*DeferredConfirmation, error) {
if err := msg.Headers.Validate(); err != nil { if err := msg.Headers.Validate(); err != nil {
return err return nil, err
} }
ch.m.Lock() ch.m.Lock()
defer ch.m.Unlock() defer ch.m.Unlock()
var dc *DeferredConfirmation
if ch.confirming {
dc = ch.confirms.publish()
}
if err := ch.send(&basicPublish{ if err := ch.send(&basicPublish{
Exchange: exchange, Exchange: exchange,
RoutingKey: key, RoutingKey: key,
@@ -1353,14 +1568,26 @@ func (ch *Channel) Publish(exchange, key string, mandatory, immediate bool, msg
AppId: msg.AppId, AppId: msg.AppId,
}, },
}); err != nil { }); err != nil {
return err if ch.confirming {
ch.confirms.unpublish()
}
return nil, err
} }
if ch.confirming { return dc, nil
ch.confirms.Publish() }
}
return nil /*
PublishWithDeferredConfirmWithContext behaves identically to Publish but additionally returns a
DeferredConfirmation, allowing the caller to wait on the publisher confirmation
for this message. If the channel has not been put into confirm mode,
the DeferredConfirmation will be nil.
NOTE: PublishWithDeferredConfirmWithContext is equivalent to its non-context variant. The context passed
to this function is not honoured.
*/
func (ch *Channel) PublishWithDeferredConfirmWithContext(_ context.Context, exchange, key string, mandatory, immediate bool, msg Publishing) (*DeferredConfirmation, error) {
return ch.PublishWithDeferredConfirm(exchange, key, mandatory, immediate, msg)
} }
/* /*
@@ -1379,7 +1606,6 @@ delivery.
When autoAck is true, the server will automatically acknowledge this message so 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 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. 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) { func (ch *Channel) Get(queue string, autoAck bool) (msg Delivery, ok bool, err error) {
req := &basicGet{Queue: queue, NoAck: autoAck} req := &basicGet{Queue: queue, NoAck: autoAck}
@@ -1411,7 +1637,6 @@ the channel is in a transaction is not defined.
Once a channel has been put into transaction mode, it cannot be taken out of 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. transaction mode. Use a different channel for non-transactional semantics.
*/ */
func (ch *Channel) Tx() error { func (ch *Channel) Tx() error {
return ch.call( return ch.call(
@@ -1425,7 +1650,6 @@ TxCommit atomically commits all publishings and acknowledgments for a single
queue and immediately start a new transaction. queue and immediately start a new transaction.
Calling this method without having called Channel.Tx is an error. Calling this method without having called Channel.Tx is an error.
*/ */
func (ch *Channel) TxCommit() error { func (ch *Channel) TxCommit() error {
return ch.call( return ch.call(
@@ -1439,7 +1663,6 @@ TxRollback atomically rolls back all publishings and acknowledgments for a
single queue and immediately start a new transaction. single queue and immediately start a new transaction.
Calling this method without having called Channel.Tx is an error. Calling this method without having called Channel.Tx is an error.
*/ */
func (ch *Channel) TxRollback() error { func (ch *Channel) TxRollback() error {
return ch.call( return ch.call(
@@ -1469,7 +1692,6 @@ pause its publishings when `false` is sent on that channel.
Note: RabbitMQ prefers to use TCP push back to control flow for all channels on 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 a connection, so under high volume scenarios, it's wise to open separate
Connections for publishings and deliveries. Connections for publishings and deliveries.
*/ */
func (ch *Channel) Flow(active bool) error { func (ch *Channel) Flow(active bool) error {
return ch.call( return ch.call(
@@ -1501,7 +1723,6 @@ persisting the message if necessary.
When noWait is true, the client will not wait for a response. A channel 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. exception could occur if the server does not support this method.
*/ */
func (ch *Channel) Confirm(noWait bool) error { func (ch *Channel) Confirm(noWait bool) error {
if err := ch.call( if err := ch.call(
@@ -1530,6 +1751,11 @@ If the deliveries cannot be recovered, an error will be returned and the channel
will be closed. will be closed.
Note: this method is not implemented on RabbitMQ, use Delivery.Nack instead Note: this method is not implemented on RabbitMQ, use Delivery.Nack instead
Deprecated: This method is deprecated in RabbitMQ. RabbitMQ used Recover(true)
as a mechanism for consumers to tell the broker that they were ready for more
deliveries, back in 2008-2009. Support for this will be removed from RabbitMQ in
a future release. Use Nack() with requeue=true instead.
*/ */
func (ch *Channel) Recover(requeue bool) error { func (ch *Channel) Recover(requeue bool) error {
return ch.call( return ch.call(
@@ -1591,3 +1817,12 @@ func (ch *Channel) Reject(tag uint64, requeue bool) error {
Requeue: requeue, Requeue: requeue,
}) })
} }
// GetNextPublishSeqNo returns the sequence number of the next message to be
// published, when in confirm mode.
func (ch *Channel) GetNextPublishSeqNo() uint64 {
ch.confirms.publishedMut.Lock()
defer ch.confirms.publishedMut.Unlock()
return ch.confirms.published + 1
}

238
vendor/github.com/rabbitmq/amqp091-go/confirms.go generated vendored Normal file
View File

@@ -0,0 +1,238 @@
// Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package amqp091
import (
"context"
"sync"
)
// confirms resequences and notifies one or multiple publisher confirmation listeners
type confirms struct {
m sync.Mutex
listeners []chan Confirmation
sequencer map[uint64]Confirmation
deferredConfirmations *deferredConfirmations
published uint64
publishedMut sync.Mutex
expecting uint64
}
// newConfirms allocates a confirms
func newConfirms() *confirms {
return &confirms{
sequencer: map[uint64]Confirmation{},
deferredConfirmations: newDeferredConfirmations(),
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() *DeferredConfirmation {
c.publishedMut.Lock()
defer c.publishedMut.Unlock()
c.published++
return c.deferredConfirmations.Add(c.published)
}
// unpublish decrements the publishing counter and removes the
// DeferredConfirmation. It must be called immediately after a publish fails.
func (c *confirms) unpublish() {
c.publishedMut.Lock()
defer c.publishedMut.Unlock()
c.deferredConfirmations.remove(c.published)
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() {
c.publishedMut.Lock()
defer c.publishedMut.Unlock()
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()
c.deferredConfirmations.Confirm(confirmed)
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()
c.deferredConfirmations.ConfirmMultiple(confirmed)
for c.expecting <= confirmed.DeliveryTag {
c.confirm(Confirmation{c.expecting, confirmed.Ack})
}
c.resequence()
}
// Cleans up the confirms struct and its dependencies.
// Closes all listeners, discarding any out of sequence confirmations
func (c *confirms) Close() error {
c.m.Lock()
defer c.m.Unlock()
c.deferredConfirmations.Close()
for _, l := range c.listeners {
close(l)
}
c.listeners = nil
return nil
}
type deferredConfirmations struct {
m sync.Mutex
confirmations map[uint64]*DeferredConfirmation
}
func newDeferredConfirmations() *deferredConfirmations {
return &deferredConfirmations{
confirmations: map[uint64]*DeferredConfirmation{},
}
}
func (d *deferredConfirmations) Add(tag uint64) *DeferredConfirmation {
d.m.Lock()
defer d.m.Unlock()
dc := &DeferredConfirmation{DeliveryTag: tag}
dc.done = make(chan struct{})
d.confirmations[tag] = dc
return dc
}
// remove is only used to drop a tag whose publish failed
func (d *deferredConfirmations) remove(tag uint64) {
d.m.Lock()
defer d.m.Unlock()
dc, found := d.confirmations[tag]
if !found {
return
}
close(dc.done)
delete(d.confirmations, tag)
}
func (d *deferredConfirmations) Confirm(confirmation Confirmation) {
d.m.Lock()
defer d.m.Unlock()
dc, found := d.confirmations[confirmation.DeliveryTag]
if !found {
// We should never receive a confirmation for a tag that hasn't
// been published, but a test causes this to happen.
return
}
dc.setAck(confirmation.Ack)
delete(d.confirmations, confirmation.DeliveryTag)
}
func (d *deferredConfirmations) ConfirmMultiple(confirmation Confirmation) {
d.m.Lock()
defer d.m.Unlock()
for k, v := range d.confirmations {
if k <= confirmation.DeliveryTag {
v.setAck(confirmation.Ack)
delete(d.confirmations, k)
}
}
}
// Close nacks all pending DeferredConfirmations being blocked by dc.Wait().
func (d *deferredConfirmations) Close() {
d.m.Lock()
defer d.m.Unlock()
for k, v := range d.confirmations {
v.setAck(false)
delete(d.confirmations, k)
}
}
// setAck sets the acknowledgement status of the confirmation. Note that it must
// not be called more than once.
func (d *DeferredConfirmation) setAck(ack bool) {
d.ack = ack
close(d.done)
}
// Done returns the channel that can be used to wait for the publisher
// confirmation.
func (d *DeferredConfirmation) Done() <-chan struct{} {
return d.done
}
// Acked returns the publisher confirmation in a non-blocking manner. It returns
// false if the confirmation was not acknowledged yet or received negative
// acknowledgement.
func (d *DeferredConfirmation) Acked() bool {
select {
case <-d.done:
default:
return false
}
return d.ack
}
// Wait blocks until the publisher confirmation. It returns true if the server
// successfully received the publishing.
func (d *DeferredConfirmation) Wait() bool {
<-d.done
return d.ack
}
// WaitContext waits until the publisher confirmation. It returns true if the
// server successfully received the publishing. If the context expires before
// that, ctx.Err() is returned.
func (d *DeferredConfirmation) WaitContext(ctx context.Context) (bool, error) {
select {
case <-ctx.Done():
return false, ctx.Err()
case <-d.done:
}
return d.ack, nil
}

View File

@@ -1,15 +1,19 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"bufio" "bufio"
"crypto/tls" "crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io" "io"
"net" "net"
"os"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@@ -23,11 +27,12 @@ const (
defaultHeartbeat = 10 * time.Second defaultHeartbeat = 10 * time.Second
defaultConnectionTimeout = 30 * time.Second defaultConnectionTimeout = 30 * time.Second
defaultProduct = "https://github.com/streadway/amqp" defaultProduct = "AMQP 0.9.1 Client"
defaultVersion = "β" buildVersion = "1.10.0"
platform = "golang"
// Safer default that makes channel leaks a lot easier to spot // Safer default that makes channel leaks a lot easier to spot
// before they create operational headaches. See https://github.com/rabbitmq/rabbitmq-server/issues/1593. // before they create operational headaches. See https://github.com/rabbitmq/rabbitmq-server/issues/1593.
defaultChannelMax = (2 << 10) - 1 defaultChannelMax = uint16((2 << 10) - 1)
defaultLocale = "en_US" defaultLocale = "en_US"
) )
@@ -44,7 +49,7 @@ type Config struct {
// bindings on the server. Dial sets this to the path parsed from the URL. // bindings on the server. Dial sets this to the path parsed from the URL.
Vhost string Vhost string
ChannelMax int // 0 max channels means 2^16 - 1 ChannelMax uint16 // 0 max channels means 2^16 - 1
FrameSize int // 0 max bytes means unlimited FrameSize int // 0 max bytes means unlimited
Heartbeat time.Duration // less than 1s uses the server's interval Heartbeat time.Duration // less than 1s uses the server's interval
@@ -71,6 +76,17 @@ type Config struct {
Dial func(network, addr string) (net.Conn, error) Dial func(network, addr string) (net.Conn, error)
} }
// NewConnectionProperties creates an amqp.Table to be used as amqp.Config.Properties.
//
// Defaults to library-defined values. For empty properties, use make(amqp.Table) instead.
func NewConnectionProperties() Table {
return Table{
"product": defaultProduct,
"version": buildVersion,
"platform": platform,
}
}
// Connection manages the serialization and deserialization of frames from IO // Connection manages the serialization and deserialization of frames from IO
// and dispatches the frames to the appropriate channel. All RPC methods and // and dispatches the frames to the appropriate channel. All RPC methods and
// asynchronous Publishing, Delivery, Ack, Nack and Return messages are // asynchronous Publishing, Delivery, Ack, Nack and Return messages are
@@ -96,6 +112,8 @@ type Connection struct {
blocks []chan Blocking blocks []chan Blocking
errors chan *Error errors chan *Error
// if connection is closed should close this chan
close chan struct{}
Config Config // The negotiated Config after connection.open Config Config // The negotiated Config after connection.open
@@ -139,8 +157,7 @@ func DefaultDial(connectionTimeout time.Duration) func(network, addr string) (ne
// scheme. It is equivalent to calling DialTLS(amqp, nil). // scheme. It is equivalent to calling DialTLS(amqp, nil).
func Dial(url string) (*Connection, error) { func Dial(url string) (*Connection, error) {
return DialConfig(url, Config{ return DialConfig(url, Config{
Heartbeat: defaultHeartbeat, Locale: defaultLocale,
Locale: defaultLocale,
}) })
} }
@@ -151,16 +168,33 @@ func Dial(url string) (*Connection, error) {
// DialTLS uses the provided tls.Config when encountering an amqps:// scheme. // DialTLS uses the provided tls.Config when encountering an amqps:// scheme.
func DialTLS(url string, amqps *tls.Config) (*Connection, error) { func DialTLS(url string, amqps *tls.Config) (*Connection, error) {
return DialConfig(url, Config{ return DialConfig(url, Config{
Heartbeat: defaultHeartbeat,
TLSClientConfig: amqps, TLSClientConfig: amqps,
Locale: defaultLocale, Locale: defaultLocale,
}) })
} }
// DialTLS_ExternalAuth accepts a string in the AMQP URI format and returns a
// new Connection over TCP using EXTERNAL auth. Defaults to a server heartbeat
// interval of 10 seconds and sets the initial read deadline to 30 seconds.
//
// This mechanism is used, when RabbitMQ is configured for EXTERNAL auth with
// ssl_cert_login plugin for userless/passwordless logons
//
// DialTLS_ExternalAuth uses the provided tls.Config when encountering an
// amqps:// scheme.
func DialTLS_ExternalAuth(url string, amqps *tls.Config) (*Connection, error) {
return DialConfig(url, Config{
TLSClientConfig: amqps,
SASL: []Authentication{&ExternalAuth{}},
})
}
// DialConfig accepts a string in the AMQP URI format and a configuration for // DialConfig accepts a string in the AMQP URI format and a configuration for
// the transport and connection setup, returning a new Connection. Defaults to // the transport and connection setup, returning a new Connection. Defaults to
// a server heartbeat interval of 10 seconds and sets the initial read deadline // a server heartbeat interval of 10 seconds and sets the initial read deadline
// to 30 seconds. // to 30 seconds. The heartbeat interval specified in the AMQP URI takes precedence
// over the value specified in the config. To disable heartbeats, you must use
// the AMQP URI and set heartbeat=0 there.
func DialConfig(url string, config Config) (*Connection, error) { func DialConfig(url string, config Config) (*Connection, error) {
var err error var err error
var conn net.Conn var conn net.Conn
@@ -171,18 +205,50 @@ func DialConfig(url string, config Config) (*Connection, error) {
} }
if config.SASL == nil { if config.SASL == nil {
config.SASL = []Authentication{uri.PlainAuth()} if uri.AuthMechanism != nil {
for _, identifier := range uri.AuthMechanism {
switch strings.ToUpper(identifier) {
case "PLAIN":
config.SASL = append(config.SASL, uri.PlainAuth())
case "AMQPLAIN":
config.SASL = append(config.SASL, uri.AMQPlainAuth())
case "EXTERNAL":
config.SASL = append(config.SASL, &ExternalAuth{})
default:
return nil, fmt.Errorf("unsupported auth_mechanism: %v", identifier)
}
}
} else {
config.SASL = []Authentication{uri.PlainAuth()}
}
} }
if config.Vhost == "" { if config.Vhost == "" {
config.Vhost = uri.Vhost config.Vhost = uri.Vhost
} }
if uri.Heartbeat.hasValue {
config.Heartbeat = uri.Heartbeat.value
} else {
if config.Heartbeat == 0 {
config.Heartbeat = defaultHeartbeat
}
}
if config.ChannelMax == 0 {
config.ChannelMax = uri.ChannelMax
}
connectionTimeout := defaultConnectionTimeout
if uri.ConnectionTimeout != 0 {
connectionTimeout = time.Duration(uri.ConnectionTimeout) * time.Millisecond
}
addr := net.JoinHostPort(uri.Host, strconv.FormatInt(int64(uri.Port), 10)) addr := net.JoinHostPort(uri.Host, strconv.FormatInt(int64(uri.Port), 10))
dialer := config.Dial dialer := config.Dial
if dialer == nil { if dialer == nil {
dialer = DefaultDial(defaultConnectionTimeout) dialer = DefaultDial(connectionTimeout)
} }
conn, err = dialer("tcp", addr) conn, err = dialer("tcp", addr)
@@ -192,7 +258,11 @@ func DialConfig(url string, config Config) (*Connection, error) {
if uri.Scheme == "amqps" { if uri.Scheme == "amqps" {
if config.TLSClientConfig == nil { if config.TLSClientConfig == nil {
config.TLSClientConfig = new(tls.Config) tlsConfig, err := tlsConfigFromURI(uri)
if err != nil {
return nil, fmt.Errorf("create TLS config from URI: %w", err)
}
config.TLSClientConfig = tlsConfig
} }
// If ServerName has not been specified in TLSClientConfig, // If ServerName has not been specified in TLSClientConfig,
@@ -203,7 +273,6 @@ func DialConfig(url string, config Config) (*Connection, error) {
client := tls.Client(conn, config.TLSClientConfig) client := tls.Client(conn, config.TLSClientConfig)
if err := client.Handshake(); err != nil { if err := client.Handshake(); err != nil {
conn.Close() conn.Close()
return nil, err return nil, err
} }
@@ -218,7 +287,6 @@ func DialConfig(url string, config Config) (*Connection, error) {
Open accepts an already established connection, or other io.ReadWriteCloser as 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 a transport. Use this method if you have established a TLS connection or wish
to use your own custom transport. to use your own custom transport.
*/ */
func Open(conn io.ReadWriteCloser, config Config) (*Connection, error) { func Open(conn io.ReadWriteCloser, config Config) (*Connection, error) {
c := &Connection{ c := &Connection{
@@ -228,12 +296,29 @@ func Open(conn io.ReadWriteCloser, config Config) (*Connection, error) {
rpc: make(chan message), rpc: make(chan message),
sends: make(chan time.Time), sends: make(chan time.Time),
errors: make(chan *Error, 1), errors: make(chan *Error, 1),
close: make(chan struct{}),
deadlines: make(chan readDeadliner, 1), deadlines: make(chan readDeadliner, 1),
} }
go c.reader(conn) go c.reader(conn)
return c, c.open(config) return c, c.open(config)
} }
/*
UpdateSecret updates the secret used to authenticate this connection. It is used when
secrets have an expiration date and need to be renewed, like OAuth 2 tokens.
It returns an error if the operation is not successful, or if the connection is closed.
*/
func (c *Connection) UpdateSecret(newSecret, reason string) error {
if c.IsClosed() {
return ErrClosed
}
return c.call(&connectionUpdateSecret{
NewSecret: newSecret,
Reason: reason,
}, &connectionUpdateSecretOk{})
}
/* /*
LocalAddr returns the local TCP peer address, or ":0" (the zero value of net.TCPAddr) 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(). as a fallback default value if the underlying transport does not support LocalAddr().
@@ -247,6 +332,18 @@ func (c *Connection) LocalAddr() net.Addr {
return &net.TCPAddr{} return &net.TCPAddr{}
} }
/*
RemoteAddr returns the remote TCP peer address, if known.
*/
func (c *Connection) RemoteAddr() net.Addr {
if conn, ok := c.conn.(interface {
RemoteAddr() net.Addr
}); ok {
return conn.RemoteAddr()
}
return &net.TCPAddr{}
}
// ConnectionState returns basic TLS details of the underlying transport. // ConnectionState returns basic TLS details of the underlying transport.
// Returns a zero value when the underlying connection does not implement // Returns a zero value when the underlying connection does not implement
// ConnectionState() tls.ConnectionState. // ConnectionState() tls.ConnectionState.
@@ -263,11 +360,14 @@ func (c *Connection) ConnectionState() tls.ConnectionState {
NotifyClose registers a listener for close events either initiated by an error NotifyClose registers a listener for close events either initiated by an error
accompanying a connection.close method or by a normal shutdown. accompanying a connection.close method or by a normal shutdown.
On normal shutdowns, the chan will be closed. The chan provided will be closed when the Connection is closed and on a
graceful close, no error will be sent.
In case of a non graceful close the error will be notified synchronously by the library
so that it will be necessary to consume the Channel from the caller in order to avoid deadlocks
To reconnect after a transport or protocol error, register a listener here and To reconnect after a transport or protocol error, register a listener here and
re-run your setup process. re-run your setup process.
*/ */
func (c *Connection) NotifyClose(receiver chan *Error) chan *Error { func (c *Connection) NotifyClose(receiver chan *Error) chan *Error {
c.m.Lock() c.m.Lock()
@@ -291,7 +391,6 @@ become free again.
This optional extension is supported by the server when the This optional extension is supported by the server when the
"connection.blocked" server capability key is true. "connection.blocked" server capability key is true.
*/ */
func (c *Connection) NotifyBlocked(receiver chan Blocking) chan Blocking { func (c *Connection) NotifyBlocked(receiver chan Blocking) chan Blocking {
c.m.Lock() c.m.Lock()
@@ -334,12 +433,47 @@ func (c *Connection) Close() error {
) )
} }
// CloseDeadline requests and waits for the response to close this AMQP connection.
//
// Accepts a deadline for waiting the server response. The deadline is passed
// to the low-level connection i.e. network socket.
//
// Regardless of the error returned, the connection is considered closed, and it
// should not be used after calling this function.
//
// In the event of an I/O timeout, connection-closed listeners are NOT informed.
//
// 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) CloseDeadline(deadline time.Time) error {
if c.IsClosed() {
return ErrClosed
}
defer c.shutdown(nil)
err := c.setDeadline(deadline)
if err != nil {
return err
}
return c.call(
&connectionClose{
ReplyCode: replySuccess,
ReplyText: "kthxbai",
},
&connectionCloseOk{},
)
}
func (c *Connection) closeWith(err *Error) error { func (c *Connection) closeWith(err *Error) error {
if c.IsClosed() { if c.IsClosed() {
return ErrClosed return ErrClosed
} }
defer c.shutdown(err) defer c.shutdown(err)
return c.call( return c.call(
&connectionClose{ &connectionClose{
ReplyCode: uint16(err.Code), ReplyCode: uint16(err.Code),
@@ -352,7 +486,19 @@ func (c *Connection) closeWith(err *Error) error {
// IsClosed returns true if the connection is marked as closed, otherwise false // IsClosed returns true if the connection is marked as closed, otherwise false
// is returned. // is returned.
func (c *Connection) IsClosed() bool { func (c *Connection) IsClosed() bool {
return (atomic.LoadInt32(&c.closed) == 1) return atomic.LoadInt32(&c.closed) == 1
}
// setDeadline is a wrapper to type assert Connection.conn and set an I/O
// deadline in the underlying TCP connection socket, by calling
// net.Conn.SetDeadline(). It returns an error, in case the type assertion fails,
// although this should never happen.
func (c *Connection) setDeadline(t time.Time) error {
con, ok := c.conn.(net.Conn)
if !ok {
return errInvalidTypeAssertion
}
return con.SetDeadline(t)
} }
func (c *Connection) send(f frame) error { func (c *Connection) send(f frame) error {
@@ -383,6 +529,74 @@ func (c *Connection) send(f frame) error {
return err return err
} }
// This method is intended to be used with sendUnflushed() to end a sequence
// of sendUnflushed() calls and flush the connection
func (c *Connection) endSendUnflushed() error {
c.sendM.Lock()
defer c.sendM.Unlock()
return c.flush()
}
// sendUnflushed performs an *Unflushed* write. It is otherwise equivalent to
// send(), and we provide a separate flush() function to explicitly flush the
// buffer after all Frames are written.
//
// Why is this a thing?
//
// send() method uses writer.WriteFrame(), which will write the Frame then
// flush the buffer. For cases like the sendOpen() method on Channel, which
// sends multiple Frames (methodFrame, headerFrame, N x bodyFrame), flushing
// after each Frame is inefficient as it negates much of the benefit of using a
// buffered writer, and results in more syscalls than necessary. Flushing buffers
// after every frame can have a significant performance impact when sending
// (basicPublish) small messages, so this method performs an *Unflushed* write
// but is otherwise equivalent to send() method, and we provide a separate
// flush method to explicitly flush the buffer after all Frames are written.
func (c *Connection) sendUnflushed(f frame) error {
if c.IsClosed() {
return ErrClosed
}
c.sendM.Lock()
err := c.writer.WriteFrameNoFlush(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(),
})
}
return err
}
// This method is intended to be used with sendUnflushed() to explicitly flush
// the buffer after all required Frames have been written to the buffer.
func (c *Connection) flush() (err error) {
if buf, ok := c.writer.w.(*bufio.Writer); ok {
err = buf.Flush()
// Moving send notifier to flush increases basicPublish for the small message
// case. As sendUnflushed + flush is used for the case of sending semantically
// related Frames (e.g. a Message like basicPublish) there is no real advantage
// to sending per Frame vice per "group of related Frames" and for the case of
// small messages time.Now() is (relatively) expensive.
if err == nil {
// 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
}
func (c *Connection) shutdown(err *Error) { func (c *Connection) shutdown(err *Error) {
atomic.StoreInt32(&c.closed, 1) atomic.StoreInt32(&c.closed, 1)
@@ -394,9 +608,6 @@ func (c *Connection) shutdown(err *Error) {
for _, c := range c.closes { for _, c := range c.closes {
c <- err c <- err
} }
}
if err != nil {
c.errors <- err c.errors <- err
} }
// Shutdown handler goroutine can still receive the result. // Shutdown handler goroutine can still receive the result.
@@ -420,9 +631,11 @@ func (c *Connection) shutdown(err *Error) {
} }
c.conn.Close() c.conn.Close()
// reader exit
close(c.close)
c.channels = map[uint16]*Channel{} c.channels = nil
c.allocator = newAllocator(1, c.Config.ChannelMax) c.allocator = nil
c.noNotify = true c.noNotify = true
}) })
} }
@@ -443,11 +656,10 @@ func (c *Connection) dispatch0(f frame) {
switch m := mf.Method.(type) { switch m := mf.Method.(type) {
case *connectionClose: case *connectionClose:
// Send immediately as shutdown will close our side of the writer. // Send immediately as shutdown will close our side of the writer.
c.send(&methodFrame{ f := &methodFrame{ChannelId: 0, Method: &connectionCloseOk{}}
ChannelId: 0, if err := c.send(f); err != nil {
Method: &connectionCloseOk{}, Logger.Printf("error sending connectionCloseOk, error: %+v", err)
}) }
c.shutdown(newError(m.ReplyCode, m.ReplyText)) c.shutdown(newError(m.ReplyCode, m.ReplyText))
case *connectionBlocked: case *connectionBlocked:
for _, c := range c.blocks { for _, c := range c.blocks {
@@ -458,22 +670,39 @@ func (c *Connection) dispatch0(f frame) {
c <- Blocking{Active: false} c <- Blocking{Active: false}
} }
default: default:
c.rpc <- m select {
case <-c.close:
return
case c.rpc <- m:
}
} }
case *heartbeatFrame: case *heartbeatFrame:
// kthx - all reads reset our deadline. so we can drop this // kthx - all reads reset our deadline. so we can drop this
default: default:
// lolwat - channel0 only responds to methods and heartbeats // lolwat - channel0 only responds to methods and heartbeats
c.closeWith(ErrUnexpectedFrame) // closeWith use call don't block reader
go func() {
if err := c.closeWith(ErrUnexpectedFrame); err != nil {
Logger.Printf("error sending connectionCloseOk with ErrUnexpectedFrame, error: %+v", err)
}
}()
} }
} }
func (c *Connection) dispatchN(f frame) { func (c *Connection) dispatchN(f frame) {
c.m.Lock() c.m.Lock()
channel := c.channels[f.channel()] channel, ok := c.channels[f.channel()]
if ok {
updateChannel(f, channel)
} else {
Logger.Printf("[debug] dropping frame, channel %d does not exist", f.channel())
}
c.m.Unlock() c.m.Unlock()
if channel != nil { // Note: this could result in concurrent dispatch depending on
// how channels are managed in an application
if ok {
channel.recv(channel, f) channel.recv(channel, f)
} else { } else {
c.dispatchClosed(f) c.dispatchClosed(f)
@@ -496,15 +725,20 @@ func (c *Connection) dispatchClosed(f frame) {
if mf, ok := f.(*methodFrame); ok { if mf, ok := f.(*methodFrame); ok {
switch mf.Method.(type) { switch mf.Method.(type) {
case *channelClose: case *channelClose:
c.send(&methodFrame{ f := &methodFrame{ChannelId: f.channel(), Method: &channelCloseOk{}}
ChannelId: f.channel(), if err := c.send(f); err != nil {
Method: &channelCloseOk{}, Logger.Printf("error sending channelCloseOk, channel id: %d error: %+v", f.channel(), err)
}) }
case *channelCloseOk: case *channelCloseOk:
// we are already closed, so do nothing // we are already closed, so do nothing
default: default:
// unexpected method on closed channel // unexpected method on closed channel
c.closeWith(ErrClosed) // closeWith use call don't block reader
go func() {
if err := c.closeWith(ErrClosed); err != nil {
Logger.Printf("error sending connectionCloseOk with ErrClosed, error: %+v", err)
}
}()
} }
} }
} }
@@ -517,6 +751,8 @@ func (c *Connection) reader(r io.Reader) {
frames := &reader{buf} frames := &reader{buf}
conn, haveDeadliner := r.(readDeadliner) conn, haveDeadliner := r.(readDeadliner)
defer close(c.rpc)
for { for {
frame, err := frames.ReadFrame() frame, err := frames.ReadFrame()
@@ -576,7 +812,13 @@ func (c *Connection) heartbeater(interval time.Duration, done chan *Error) {
// When reading, reset our side of the deadline, if we've negotiated one with // When reading, reset our side of the deadline, if we've negotiated one with
// a deadline that covers at least 2 server heartbeats // a deadline that covers at least 2 server heartbeats
if interval > 0 { if interval > 0 {
conn.SetReadDeadline(time.Now().Add(maxServerHeartbeatsInFlight * interval)) if err := conn.SetReadDeadline(time.Now().Add(maxServerHeartbeatsInFlight * interval)); err != nil {
var opErr *net.OpError
if !errors.As(err, &opErr) {
Logger.Printf("error setting read deadline in heartbeater: %+v", err)
return
}
}
} }
case <-done: case <-done:
@@ -618,12 +860,17 @@ func (c *Connection) allocateChannel() (*Channel, error) {
// releaseChannel removes a channel from the registry as the final part of the // releaseChannel removes a channel from the registry as the final part of the
// channel lifecycle // channel lifecycle
func (c *Connection) releaseChannel(id uint16) { func (c *Connection) releaseChannel(ch *Channel) {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
delete(c.channels, id) if !c.IsClosed() {
c.allocator.release(int(id)) got, ok := c.channels[ch.id]
if ok && got == ch {
delete(c.channels, ch.id)
c.allocator.release(int(ch.id))
}
}
} }
// openChannel allocates and opens a channel, must be paired with closeChannel // openChannel allocates and opens a channel, must be paired with closeChannel
@@ -634,7 +881,7 @@ func (c *Connection) openChannel() (*Channel, error) {
} }
if err := ch.open(); err != nil { if err := ch.open(); err != nil {
c.releaseChannel(ch.id) c.releaseChannel(ch)
return nil, err return nil, err
} }
return ch, nil return ch, nil
@@ -645,14 +892,13 @@ func (c *Connection) openChannel() (*Channel, error) {
// this connection. // this connection.
func (c *Connection) closeChannel(ch *Channel, e *Error) { func (c *Connection) closeChannel(ch *Channel, e *Error) {
ch.shutdown(e) ch.shutdown(e)
c.releaseChannel(ch.id) c.releaseChannel(ch)
} }
/* /*
Channel opens a unique, concurrent server channel to process the bulk of AMQP 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 messages. Any error from methods on this receiver will render the receiver
invalid and a new Channel should be opened. invalid and a new Channel should be opened.
*/ */
func (c *Connection) Channel() (*Channel, error) { func (c *Connection) Channel() (*Channel, error) {
return c.openChannel() return c.openChannel()
@@ -667,39 +913,46 @@ func (c *Connection) call(req message, res ...message) error {
} }
} }
var msg message
select { select {
case err, ok := <-c.errors: case e, ok := <-c.errors:
if !ok { if ok {
return ErrClosed return e
} }
return err return ErrClosed
case msg = <-c.rpc:
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
// 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
} }
// Connection = open-Connection *use-Connection close-Connection // Communication flow to open, use and close a connection. 'C:' are
// open-Connection = C:protocol-header // frames sent by the Client. 'S:' are frames sent by the Server.
// S:START C:START-OK //
// *challenge // Connection = open-Connection *use-Connection close-Connection
// S:TUNE C:TUNE-OK //
// C:OPEN S:OPEN-OK // open-Connection = C:protocol-header
// challenge = S:SECURE C:SECURE-OK // S:START C:START-OK
// use-Connection = *channel // *challenge
// close-Connection = C:CLOSE S:CLOSE-OK // S:TUNE C:TUNE-OK
// / S:CLOSE C:CLOSE-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 { func (c *Connection) open(config Config) error {
if err := c.send(&protocolHeader{}); err != nil { if err := c.send(&protocolHeader{}); err != nil {
return err return err
@@ -717,7 +970,7 @@ func (c *Connection) openStart(config Config) error {
c.Major = int(start.VersionMajor) c.Major = int(start.VersionMajor)
c.Minor = int(start.VersionMinor) c.Minor = int(start.VersionMinor)
c.Properties = Table(start.ServerProperties) c.Properties = start.ServerProperties
c.Locales = strings.Split(start.Locales, " ") c.Locales = strings.Split(start.Locales, " ")
// eventually support challenge/response here by also responding to // eventually support challenge/response here by also responding to
@@ -738,15 +991,14 @@ func (c *Connection) openStart(config Config) error {
func (c *Connection) openTune(config Config, auth Authentication) error { func (c *Connection) openTune(config Config, auth Authentication) error {
if len(config.Properties) == 0 { if len(config.Properties) == 0 {
config.Properties = Table{ config.Properties = NewConnectionProperties()
"product": defaultProduct,
"version": defaultVersion,
}
} }
config.Properties["capabilities"] = Table{ config.Properties["capabilities"] = Table{
"connection.blocked": true, "connection.blocked": true,
"consumer_cancel_notify": true, "consumer_cancel_notify": true,
"basic.nack": true,
"publisher_confirms": true,
} }
ok := &connectionStartOk{ ok := &connectionStartOk{
@@ -764,13 +1016,21 @@ func (c *Connection) openTune(config Config, auth Authentication) error {
return ErrCredentials return ErrCredentials
} }
// Edge case that may race with c.shutdown()
// https://github.com/rabbitmq/amqp091-go/issues/170
c.m.Lock()
// When the server and client both use default 0, then the max channel is // When the server and client both use default 0, then the max channel is
// only limited by uint16. // only limited by uint16.
c.Config.ChannelMax = pick(config.ChannelMax, int(tune.ChannelMax)) c.Config.ChannelMax = pickUInt16(config.ChannelMax, tune.ChannelMax)
if c.Config.ChannelMax == 0 { if c.Config.ChannelMax == 0 {
c.Config.ChannelMax = defaultChannelMax c.Config.ChannelMax = defaultChannelMax
} }
c.Config.ChannelMax = min(c.Config.ChannelMax, maxChannelMax) c.Config.ChannelMax = minUInt16(c.Config.ChannelMax, maxChannelMax)
c.allocator = newAllocator(1, int(c.Config.ChannelMax))
c.m.Unlock()
// Frame size includes headers and end byte (len(payload)+8), even if // Frame size includes headers and end byte (len(payload)+8), even if
// this is less than FrameMinSize, use what the server sends because the // this is less than FrameMinSize, use what the server sends because the
@@ -784,7 +1044,7 @@ func (c *Connection) openTune(config Config, auth Authentication) error {
// "The client should start sending heartbeats after receiving a // "The client should start sending heartbeats after receiving a
// Connection.Tune method" // Connection.Tune method"
go c.heartbeater(c.Config.Heartbeat, c.NotifyClose(make(chan *Error, 1))) go c.heartbeater(c.Config.Heartbeat/2, c.NotifyClose(make(chan *Error, 1)))
if err := c.send(&methodFrame{ if err := c.send(&methodFrame{
ChannelId: 0, ChannelId: 0,
@@ -826,10 +1086,48 @@ func (c *Connection) openComplete() error {
_ = deadliner.SetDeadline(time.Time{}) _ = deadliner.SetDeadline(time.Time{})
} }
c.allocator = newAllocator(1, c.Config.ChannelMax)
return nil return nil
} }
// tlsConfigFromURI tries to create TLS configuration based on query parameters.
// Returns default (empty) config in case no suitable client cert and/or client key not provided.
// Returns error in case certificates can not be parsed.
func tlsConfigFromURI(uri URI) (*tls.Config, error) {
var certPool *x509.CertPool
if uri.CACertFile != "" {
data, err := os.ReadFile(uri.CACertFile)
if err != nil {
return nil, fmt.Errorf("read CA certificate: %w", err)
}
certPool = x509.NewCertPool()
certPool.AppendCertsFromPEM(data)
} else if sysPool, err := x509.SystemCertPool(); err != nil {
return nil, fmt.Errorf("load system certificates: %w", err)
} else {
certPool = sysPool
}
if uri.CertFile == "" || uri.KeyFile == "" {
// no client auth (mTLS), just server auth
return &tls.Config{
RootCAs: certPool,
ServerName: uri.ServerName,
}, nil
}
certificate, err := tls.LoadX509KeyPair(uri.CertFile, uri.KeyFile)
if err != nil {
return nil, fmt.Errorf("load client certificate: %w", err)
}
return &tls.Config{
Certificates: []tls.Certificate{certificate},
RootCAs: certPool,
ServerName: uri.ServerName,
}, nil
}
func max(a, b int) int { func max(a, b int) int {
if a > b { if a > b {
return a return a
@@ -837,6 +1135,13 @@ func max(a, b int) int {
return b return b
} }
func maxUInt16(a, b uint16) uint16 {
if a > b {
return a
}
return b
}
func min(a, b int) int { func min(a, b int) int {
if a < b { if a < b {
return a return a
@@ -844,6 +1149,21 @@ func min(a, b int) int {
return b return b
} }
func minUInt16(a, b uint16) uint16 {
if a < b {
return a
}
return b
}
func pickUInt16(client, server uint16) uint16 {
if client == 0 || server == 0 {
return maxUInt16(client, server)
} else {
return minUInt16(client, server)
}
}
func pick(client, server int) int { func pick(client, server int) int {
if client == 0 || server == 0 { if client == 0 || server == 0 {
return max(client, server) return max(client, server)

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"os" "os"
@@ -75,6 +75,33 @@ func (subs *consumers) buffer(in chan *Delivery, out chan Delivery) {
} }
case out <- *queue[0]: case out <- *queue[0]:
/*
* https://github.com/rabbitmq/amqp091-go/issues/179
* https://github.com/rabbitmq/amqp091-go/pull/180
*
* Comment from @lars-t-hansen:
*
* Given Go's slice semantics, and barring any information
* available to the compiler that proves that queue is the only
* pointer to the memory it references, the only meaning that
* queue = queue[1:] can have is basically queue += sizeof(queue
* element), ie, it bumps a pointer. Looking at the generated
* code for a simple example (on ARM64 in this case) bears this
* out. So what we're left with is an array that we have a
* pointer into the middle of. When the GC traces this pointer,
* it too does not know whether the array has multiple
* referents, and so its only sensible choice is to find the
* beginning of the array, and if the array is not already
* visited, mark every element in it, including the "dead"
* pointer.
*
* (Depending on the program dynamics, an element may eventually
* be appended to the queue when the queue is at capacity, and
* in this case the live elements are copied into a new array
* and the old array is left to be GC'd eventually, along with
* the dead object. But that can take time.)
*/
queue[0] = nil
queue = queue[1:] queue = queue[1:]
} }
} }

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"errors" "errors"
@@ -13,7 +13,7 @@ import (
var errDeliveryNotInitialized = errors.New("delivery not initialized") var errDeliveryNotInitialized = errors.New("delivery not initialized")
// Acknowledger notifies the server of successful or failed consumption of // Acknowledger notifies the server of successful or failed consumption of
// delivieries via identifier found in the Delivery.DeliveryTag field. // deliveries via identifier found in the Delivery.DeliveryTag field.
// //
// Applications can provide mock implementations in tests of Delivery handlers. // Applications can provide mock implementations in tests of Delivery handlers.
type Acknowledger interface { type Acknowledger interface {

160
vendor/github.com/rabbitmq/amqp091-go/doc.go generated vendored Normal file
View File

@@ -0,0 +1,160 @@
// Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package amqp091 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 of 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 synchronous 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.PublishWithContext] 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. 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
# Best practises for Connection and Channel notifications:
In order to be notified when a connection or channel gets closed, both
structures offer the possibility to register channels using
[Channel.NotifyClose] and [Connection.NotifyClose] functions:
notifyConnCloseCh := conn.NotifyClose(make(chan *amqp.Error, 1))
No errors will be sent in case of a graceful connection close. In case of a
non-graceful closure due to e.g. network issue, or forced connection closure
from the Management UI, the error will be notified synchronously by the library.
The library sends to notification channels just once. After sending a
notification to all channels, the library closes all registered notification
channels. After receiving a notification, the application should create and
register a new channel. To avoid deadlocks in the library, it is necessary to
consume from the channels. This could be done inside a different goroutine with
a select listening on the two channels inside a for loop like:
go func() {
for notifyConnClose != nil || notifyChanClose != nil {
select {
case err, ok := <-notifyConnClose:
if !ok {
notifyConnClose = nil
} else {
fmt.Printf("connection closed, error %s", err)
}
case err, ok := <-notifyChanClose:
if !ok {
notifyChanClose = nil
} else {
fmt.Printf("channel closed, error %s", err)
}
}
}
}()
It is strongly recommended to use buffered channels to avoid deadlocks inside
the library.
# Best practises for NotifyPublish notifications:
Using [Channel.NotifyPublish] allows the caller of the library to be notified,
through a go channel, when a message has been received and confirmed by the
broker. It's advisable to wait for all Confirmations to arrive before calling
[Channel.Close] or [Connection.Close]. It is also necessary to consume from this
channel until it gets closed. The library sends synchronously to the registered channel.
It is advisable to use a buffered channel, with capacity set to the maximum acceptable
number of unconfirmed messages.
It is important to consume from the confirmation channel at all times, in order to avoid
deadlocks in the library.
*/
package amqp091

23
vendor/github.com/rabbitmq/amqp091-go/fuzz.go generated vendored Normal file
View File

@@ -0,0 +1,23 @@
// Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gofuzz
// +build gofuzz
package amqp091
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
}

14
vendor/github.com/rabbitmq/amqp091-go/gen.ps1 generated vendored Normal file
View File

@@ -0,0 +1,14 @@
$DebugPreference = 'Continue'
$ErrorActionPreference = 'Stop'
Set-PSDebug -Off
Set-StrictMode -Version 'Latest' -ErrorAction 'Stop' -Verbose
New-Variable -Name curdir -Option Constant -Value $PSScriptRoot
$specDir = Resolve-Path -LiteralPath (Join-Path -Path $curdir -ChildPath 'spec')
$amqpSpecXml = Resolve-Path -LiteralPath (Join-Path -Path $specDir -ChildPath 'amqp0-9-1.stripped.extended.xml')
$gen = Resolve-Path -LiteralPath (Join-Path -Path $specDir -ChildPath 'gen.go')
$spec091 = Resolve-Path -LiteralPath (Join-Path -Path $curdir -ChildPath 'spec091.go')
Get-Content -LiteralPath $amqpSpecXml | go run $gen | gofmt | Set-Content -Force -Path $spec091

23
vendor/github.com/rabbitmq/amqp091-go/log.go generated vendored Normal file
View File

@@ -0,0 +1,23 @@
// Copyright (c) 2022 VMware, Inc. or its affiliates. All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package amqp091
type Logging interface {
Printf(format string, v ...interface{})
}
var Logger Logging = NullLogger{}
// Enables logging using a custom Logging instance. Note that this is
// not thread safe and should be called at application start
func SetLogger(logger Logging) {
Logger = logger
}
type NullLogger struct {
}
func (l NullLogger) Printf(format string, v ...interface{}) {
}

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"bytes" "bytes"
@@ -14,29 +14,29 @@ import (
) )
/* /*
Reads a frame from an input stream and returns an interface that can be cast into ReadFrame reads a frame from an input stream and returns an interface that can be cast into
one of the following: one of the following:
methodFrame methodFrame
PropertiesFrame PropertiesFrame
bodyFrame bodyFrame
heartbeatFrame heartbeatFrame
2.3.5 frame Details 2.3.5 frame Details
All frames consist of a header (7 octets), a payload of arbitrary size, and a All frames consist of a header (7 octets), a payload of arbitrary size, and a
'frame-end' octet that detects malformed frames: 'frame-end' octet that detects malformed frames:
0 1 3 7 size+7 size+8 0 1 3 7 size+7 size+8
+------+---------+-------------+ +------------+ +-----------+ +------+---------+-------------+ +------------+ +-----------+
| type | channel | size | | payload | | frame-end | | type | channel | size | | payload | | frame-end |
+------+---------+-------------+ +------------+ +-----------+ +------+---------+-------------+ +------------+ +-----------+
octet short long size octets octet octet short long size octets octet
To read a frame, we: To read a frame, we:
1. Read the header and check the frame type and channel. 1. Read the header and check the frame type and channel.
2. Depending on the frame type, we read the payload and process it. 2. Depending on the frame type, we read the payload and process it.
3. Read the frame end octet. 3. Read the frame end octet.
In realistic implementations where performance is a concern, we would use In realistic implementations where performance is a concern, we would use
read-ahead buffering or read-ahead buffering or
@@ -50,7 +50,7 @@ func (r *reader) ReadFrame() (frame frame, err error) {
return return
} }
typ := uint8(scratch[0]) typ := scratch[0]
channel := binary.BigEndian.Uint16(scratch[1:3]) channel := binary.BigEndian.Uint16(scratch[1:3])
size := binary.BigEndian.Uint32(scratch[3:7]) size := binary.BigEndian.Uint32(scratch[3:7])
@@ -131,20 +131,6 @@ func readDecimal(r io.Reader) (v Decimal, err error) {
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) { func readTimestamp(r io.Reader) (v time.Time, err error) {
var sec int64 var sec int64
if err = binary.Read(r, binary.BigEndian, &sec); err != nil { if err = binary.Read(r, binary.BigEndian, &sec); err != nil {
@@ -161,7 +147,8 @@ func readTimestamp(r io.Reader) (v time.Time, err error) {
'S': string 'S': string
'T': time.Time 'T': time.Time
'V': nil 'V': nil
'b': byte 'b': int8
'B': byte
'd': float64 'd': float64
'f': float32 'f': float32
'l': int64 'l': int64
@@ -181,15 +168,22 @@ func readField(r io.Reader) (v interface{}, err error) {
if err = binary.Read(r, binary.BigEndian, &value); err != nil { if err = binary.Read(r, binary.BigEndian, &value); err != nil {
return return
} }
return (value != 0), nil return value != 0, nil
case 'b': case 'B':
var value [1]byte var value [1]byte
if _, err = io.ReadFull(r, value[0:1]); err != nil { if _, err = io.ReadFull(r, value[0:1]); err != nil {
return return
} }
return value[0], nil return value[0], nil
case 'b':
var value int8
if err = binary.Read(r, binary.BigEndian, &value); err != nil {
return
}
return value, nil
case 's': case 's':
var value int16 var value int16
if err = binary.Read(r, binary.BigEndian, &value); err != nil { if err = binary.Read(r, binary.BigEndian, &value); err != nil {
@@ -260,12 +254,12 @@ func readField(r io.Reader) (v interface{}, err error) {
} }
/* /*
Field tables are long strings that contain packed name-value pairs. The 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 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 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 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 types, and are shown in the grammar. Multi-octet integer fields are always
held in network byte order. held in network byte order.
*/ */
func readTable(r io.Reader) (table Table, err error) { func readTable(r io.Reader) (table Table, err error) {
var nested bytes.Buffer var nested bytes.Buffer
@@ -309,7 +303,7 @@ func readArray(r io.Reader) ([]interface{}, error) {
var ( var (
lim = &io.LimitedReader{R: r, N: int64(size)} lim = &io.LimitedReader{R: r, N: int64(size)}
arr = []interface{}{} arr []interface{}
field interface{} field interface{}
) )

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"time" "time"

View File

@@ -1,12 +1,12 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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 */ /* GENERATED FILE - DO NOT EDIT */
/* Rebuild from the spec/gen.go tool */ /* Rebuild from the spec/gen.go tool */
package amqp package amqp091
import ( import (
"encoding/binary" "encoding/binary"
@@ -552,6 +552,66 @@ func (msg *connectionUnblocked) read(r io.Reader) (err error) {
return return
} }
type connectionUpdateSecret struct {
NewSecret string
Reason string
}
func (msg *connectionUpdateSecret) id() (uint16, uint16) {
return 10, 70
}
func (msg *connectionUpdateSecret) wait() bool {
return true
}
func (msg *connectionUpdateSecret) write(w io.Writer) (err error) {
if err = writeLongstr(w, msg.NewSecret); err != nil {
return
}
if err = writeShortstr(w, msg.Reason); err != nil {
return
}
return
}
func (msg *connectionUpdateSecret) read(r io.Reader) (err error) {
if msg.NewSecret, err = readLongstr(r); err != nil {
return
}
if msg.Reason, err = readShortstr(r); err != nil {
return
}
return
}
type connectionUpdateSecretOk struct {
}
func (msg *connectionUpdateSecretOk) id() (uint16, uint16) {
return 10, 71
}
func (msg *connectionUpdateSecretOk) wait() bool {
return true
}
func (msg *connectionUpdateSecretOk) write(w io.Writer) (err error) {
return
}
func (msg *connectionUpdateSecretOk) read(r io.Reader) (err error) {
return
}
type channelOpen struct { type channelOpen struct {
reserved1 string reserved1 string
} }
@@ -2757,7 +2817,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
switch mf.MethodId { switch mf.MethodId {
case 10: // connection start case 10: // connection start
//fmt.Println("NextMethod: class:10 method:10") // fmt.Println("NextMethod: class:10 method:10")
method := &connectionStart{} method := &connectionStart{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2765,7 +2825,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 11: // connection start-ok case 11: // connection start-ok
//fmt.Println("NextMethod: class:10 method:11") // fmt.Println("NextMethod: class:10 method:11")
method := &connectionStartOk{} method := &connectionStartOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2773,7 +2833,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 20: // connection secure case 20: // connection secure
//fmt.Println("NextMethod: class:10 method:20") // fmt.Println("NextMethod: class:10 method:20")
method := &connectionSecure{} method := &connectionSecure{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2781,7 +2841,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 21: // connection secure-ok case 21: // connection secure-ok
//fmt.Println("NextMethod: class:10 method:21") // fmt.Println("NextMethod: class:10 method:21")
method := &connectionSecureOk{} method := &connectionSecureOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2789,7 +2849,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 30: // connection tune case 30: // connection tune
//fmt.Println("NextMethod: class:10 method:30") // fmt.Println("NextMethod: class:10 method:30")
method := &connectionTune{} method := &connectionTune{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2797,7 +2857,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 31: // connection tune-ok case 31: // connection tune-ok
//fmt.Println("NextMethod: class:10 method:31") // fmt.Println("NextMethod: class:10 method:31")
method := &connectionTuneOk{} method := &connectionTuneOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2805,7 +2865,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 40: // connection open case 40: // connection open
//fmt.Println("NextMethod: class:10 method:40") // fmt.Println("NextMethod: class:10 method:40")
method := &connectionOpen{} method := &connectionOpen{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2813,7 +2873,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 41: // connection open-ok case 41: // connection open-ok
//fmt.Println("NextMethod: class:10 method:41") // fmt.Println("NextMethod: class:10 method:41")
method := &connectionOpenOk{} method := &connectionOpenOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2821,7 +2881,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 50: // connection close case 50: // connection close
//fmt.Println("NextMethod: class:10 method:50") // fmt.Println("NextMethod: class:10 method:50")
method := &connectionClose{} method := &connectionClose{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2829,7 +2889,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 51: // connection close-ok case 51: // connection close-ok
//fmt.Println("NextMethod: class:10 method:51") // fmt.Println("NextMethod: class:10 method:51")
method := &connectionCloseOk{} method := &connectionCloseOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2837,7 +2897,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 60: // connection blocked case 60: // connection blocked
//fmt.Println("NextMethod: class:10 method:60") // fmt.Println("NextMethod: class:10 method:60")
method := &connectionBlocked{} method := &connectionBlocked{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2845,13 +2905,29 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 61: // connection unblocked case 61: // connection unblocked
//fmt.Println("NextMethod: class:10 method:61") // fmt.Println("NextMethod: class:10 method:61")
method := &connectionUnblocked{} method := &connectionUnblocked{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
} }
mf.Method = method mf.Method = method
case 70: // connection update-secret
// fmt.Println("NextMethod: class:10 method:70")
method := &connectionUpdateSecret{}
if err = method.read(r.r); err != nil {
return
}
mf.Method = method
case 71: // connection update-secret-ok
// fmt.Println("NextMethod: class:10 method:71")
method := &connectionUpdateSecretOk{}
if err = method.read(r.r); err != nil {
return
}
mf.Method = method
default: default:
return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId) return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
} }
@@ -2860,7 +2936,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
switch mf.MethodId { switch mf.MethodId {
case 10: // channel open case 10: // channel open
//fmt.Println("NextMethod: class:20 method:10") // fmt.Println("NextMethod: class:20 method:10")
method := &channelOpen{} method := &channelOpen{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2868,7 +2944,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 11: // channel open-ok case 11: // channel open-ok
//fmt.Println("NextMethod: class:20 method:11") // fmt.Println("NextMethod: class:20 method:11")
method := &channelOpenOk{} method := &channelOpenOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2876,7 +2952,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 20: // channel flow case 20: // channel flow
//fmt.Println("NextMethod: class:20 method:20") // fmt.Println("NextMethod: class:20 method:20")
method := &channelFlow{} method := &channelFlow{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2884,7 +2960,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 21: // channel flow-ok case 21: // channel flow-ok
//fmt.Println("NextMethod: class:20 method:21") // fmt.Println("NextMethod: class:20 method:21")
method := &channelFlowOk{} method := &channelFlowOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2892,7 +2968,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 40: // channel close case 40: // channel close
//fmt.Println("NextMethod: class:20 method:40") // fmt.Println("NextMethod: class:20 method:40")
method := &channelClose{} method := &channelClose{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2900,7 +2976,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 41: // channel close-ok case 41: // channel close-ok
//fmt.Println("NextMethod: class:20 method:41") // fmt.Println("NextMethod: class:20 method:41")
method := &channelCloseOk{} method := &channelCloseOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2915,7 +2991,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
switch mf.MethodId { switch mf.MethodId {
case 10: // exchange declare case 10: // exchange declare
//fmt.Println("NextMethod: class:40 method:10") // fmt.Println("NextMethod: class:40 method:10")
method := &exchangeDeclare{} method := &exchangeDeclare{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2923,7 +2999,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 11: // exchange declare-ok case 11: // exchange declare-ok
//fmt.Println("NextMethod: class:40 method:11") // fmt.Println("NextMethod: class:40 method:11")
method := &exchangeDeclareOk{} method := &exchangeDeclareOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2931,7 +3007,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 20: // exchange delete case 20: // exchange delete
//fmt.Println("NextMethod: class:40 method:20") // fmt.Println("NextMethod: class:40 method:20")
method := &exchangeDelete{} method := &exchangeDelete{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2939,7 +3015,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 21: // exchange delete-ok case 21: // exchange delete-ok
//fmt.Println("NextMethod: class:40 method:21") // fmt.Println("NextMethod: class:40 method:21")
method := &exchangeDeleteOk{} method := &exchangeDeleteOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2947,7 +3023,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 30: // exchange bind case 30: // exchange bind
//fmt.Println("NextMethod: class:40 method:30") // fmt.Println("NextMethod: class:40 method:30")
method := &exchangeBind{} method := &exchangeBind{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2955,7 +3031,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 31: // exchange bind-ok case 31: // exchange bind-ok
//fmt.Println("NextMethod: class:40 method:31") // fmt.Println("NextMethod: class:40 method:31")
method := &exchangeBindOk{} method := &exchangeBindOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2963,7 +3039,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 40: // exchange unbind case 40: // exchange unbind
//fmt.Println("NextMethod: class:40 method:40") // fmt.Println("NextMethod: class:40 method:40")
method := &exchangeUnbind{} method := &exchangeUnbind{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2971,7 +3047,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 51: // exchange unbind-ok case 51: // exchange unbind-ok
//fmt.Println("NextMethod: class:40 method:51") // fmt.Println("NextMethod: class:40 method:51")
method := &exchangeUnbindOk{} method := &exchangeUnbindOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2986,7 +3062,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
switch mf.MethodId { switch mf.MethodId {
case 10: // queue declare case 10: // queue declare
//fmt.Println("NextMethod: class:50 method:10") // fmt.Println("NextMethod: class:50 method:10")
method := &queueDeclare{} method := &queueDeclare{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -2994,7 +3070,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 11: // queue declare-ok case 11: // queue declare-ok
//fmt.Println("NextMethod: class:50 method:11") // fmt.Println("NextMethod: class:50 method:11")
method := &queueDeclareOk{} method := &queueDeclareOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3002,7 +3078,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 20: // queue bind case 20: // queue bind
//fmt.Println("NextMethod: class:50 method:20") // fmt.Println("NextMethod: class:50 method:20")
method := &queueBind{} method := &queueBind{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3010,7 +3086,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 21: // queue bind-ok case 21: // queue bind-ok
//fmt.Println("NextMethod: class:50 method:21") // fmt.Println("NextMethod: class:50 method:21")
method := &queueBindOk{} method := &queueBindOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3018,7 +3094,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 50: // queue unbind case 50: // queue unbind
//fmt.Println("NextMethod: class:50 method:50") // fmt.Println("NextMethod: class:50 method:50")
method := &queueUnbind{} method := &queueUnbind{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3026,7 +3102,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 51: // queue unbind-ok case 51: // queue unbind-ok
//fmt.Println("NextMethod: class:50 method:51") // fmt.Println("NextMethod: class:50 method:51")
method := &queueUnbindOk{} method := &queueUnbindOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3034,7 +3110,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 30: // queue purge case 30: // queue purge
//fmt.Println("NextMethod: class:50 method:30") // fmt.Println("NextMethod: class:50 method:30")
method := &queuePurge{} method := &queuePurge{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3042,7 +3118,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 31: // queue purge-ok case 31: // queue purge-ok
//fmt.Println("NextMethod: class:50 method:31") // fmt.Println("NextMethod: class:50 method:31")
method := &queuePurgeOk{} method := &queuePurgeOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3050,7 +3126,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 40: // queue delete case 40: // queue delete
//fmt.Println("NextMethod: class:50 method:40") // fmt.Println("NextMethod: class:50 method:40")
method := &queueDelete{} method := &queueDelete{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3058,7 +3134,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 41: // queue delete-ok case 41: // queue delete-ok
//fmt.Println("NextMethod: class:50 method:41") // fmt.Println("NextMethod: class:50 method:41")
method := &queueDeleteOk{} method := &queueDeleteOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3073,7 +3149,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
switch mf.MethodId { switch mf.MethodId {
case 10: // basic qos case 10: // basic qos
//fmt.Println("NextMethod: class:60 method:10") // fmt.Println("NextMethod: class:60 method:10")
method := &basicQos{} method := &basicQos{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3081,7 +3157,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 11: // basic qos-ok case 11: // basic qos-ok
//fmt.Println("NextMethod: class:60 method:11") // fmt.Println("NextMethod: class:60 method:11")
method := &basicQosOk{} method := &basicQosOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3089,7 +3165,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 20: // basic consume case 20: // basic consume
//fmt.Println("NextMethod: class:60 method:20") // fmt.Println("NextMethod: class:60 method:20")
method := &basicConsume{} method := &basicConsume{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3097,7 +3173,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 21: // basic consume-ok case 21: // basic consume-ok
//fmt.Println("NextMethod: class:60 method:21") // fmt.Println("NextMethod: class:60 method:21")
method := &basicConsumeOk{} method := &basicConsumeOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3105,7 +3181,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 30: // basic cancel case 30: // basic cancel
//fmt.Println("NextMethod: class:60 method:30") // fmt.Println("NextMethod: class:60 method:30")
method := &basicCancel{} method := &basicCancel{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3113,7 +3189,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 31: // basic cancel-ok case 31: // basic cancel-ok
//fmt.Println("NextMethod: class:60 method:31") // fmt.Println("NextMethod: class:60 method:31")
method := &basicCancelOk{} method := &basicCancelOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3121,7 +3197,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 40: // basic publish case 40: // basic publish
//fmt.Println("NextMethod: class:60 method:40") // fmt.Println("NextMethod: class:60 method:40")
method := &basicPublish{} method := &basicPublish{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3129,7 +3205,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 50: // basic return case 50: // basic return
//fmt.Println("NextMethod: class:60 method:50") // fmt.Println("NextMethod: class:60 method:50")
method := &basicReturn{} method := &basicReturn{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3137,7 +3213,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 60: // basic deliver case 60: // basic deliver
//fmt.Println("NextMethod: class:60 method:60") // fmt.Println("NextMethod: class:60 method:60")
method := &basicDeliver{} method := &basicDeliver{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3145,7 +3221,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 70: // basic get case 70: // basic get
//fmt.Println("NextMethod: class:60 method:70") // fmt.Println("NextMethod: class:60 method:70")
method := &basicGet{} method := &basicGet{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3153,7 +3229,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 71: // basic get-ok case 71: // basic get-ok
//fmt.Println("NextMethod: class:60 method:71") // fmt.Println("NextMethod: class:60 method:71")
method := &basicGetOk{} method := &basicGetOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3161,7 +3237,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 72: // basic get-empty case 72: // basic get-empty
//fmt.Println("NextMethod: class:60 method:72") // fmt.Println("NextMethod: class:60 method:72")
method := &basicGetEmpty{} method := &basicGetEmpty{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3169,7 +3245,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 80: // basic ack case 80: // basic ack
//fmt.Println("NextMethod: class:60 method:80") // fmt.Println("NextMethod: class:60 method:80")
method := &basicAck{} method := &basicAck{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3177,7 +3253,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 90: // basic reject case 90: // basic reject
//fmt.Println("NextMethod: class:60 method:90") // fmt.Println("NextMethod: class:60 method:90")
method := &basicReject{} method := &basicReject{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3185,7 +3261,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 100: // basic recover-async case 100: // basic recover-async
//fmt.Println("NextMethod: class:60 method:100") // fmt.Println("NextMethod: class:60 method:100")
method := &basicRecoverAsync{} method := &basicRecoverAsync{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3193,7 +3269,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 110: // basic recover case 110: // basic recover
//fmt.Println("NextMethod: class:60 method:110") // fmt.Println("NextMethod: class:60 method:110")
method := &basicRecover{} method := &basicRecover{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3201,7 +3277,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 111: // basic recover-ok case 111: // basic recover-ok
//fmt.Println("NextMethod: class:60 method:111") // fmt.Println("NextMethod: class:60 method:111")
method := &basicRecoverOk{} method := &basicRecoverOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3209,7 +3285,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 120: // basic nack case 120: // basic nack
//fmt.Println("NextMethod: class:60 method:120") // fmt.Println("NextMethod: class:60 method:120")
method := &basicNack{} method := &basicNack{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3224,7 +3300,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
switch mf.MethodId { switch mf.MethodId {
case 10: // tx select case 10: // tx select
//fmt.Println("NextMethod: class:90 method:10") // fmt.Println("NextMethod: class:90 method:10")
method := &txSelect{} method := &txSelect{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3232,7 +3308,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 11: // tx select-ok case 11: // tx select-ok
//fmt.Println("NextMethod: class:90 method:11") // fmt.Println("NextMethod: class:90 method:11")
method := &txSelectOk{} method := &txSelectOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3240,7 +3316,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 20: // tx commit case 20: // tx commit
//fmt.Println("NextMethod: class:90 method:20") // fmt.Println("NextMethod: class:90 method:20")
method := &txCommit{} method := &txCommit{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3248,7 +3324,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 21: // tx commit-ok case 21: // tx commit-ok
//fmt.Println("NextMethod: class:90 method:21") // fmt.Println("NextMethod: class:90 method:21")
method := &txCommitOk{} method := &txCommitOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3256,7 +3332,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 30: // tx rollback case 30: // tx rollback
//fmt.Println("NextMethod: class:90 method:30") // fmt.Println("NextMethod: class:90 method:30")
method := &txRollback{} method := &txRollback{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3264,7 +3340,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 31: // tx rollback-ok case 31: // tx rollback-ok
//fmt.Println("NextMethod: class:90 method:31") // fmt.Println("NextMethod: class:90 method:31")
method := &txRollbackOk{} method := &txRollbackOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3279,7 +3355,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
switch mf.MethodId { switch mf.MethodId {
case 10: // confirm select case 10: // confirm select
//fmt.Println("NextMethod: class:85 method:10") // fmt.Println("NextMethod: class:85 method:10")
method := &confirmSelect{} method := &confirmSelect{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return
@@ -3287,7 +3363,7 @@ func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err err
mf.Method = method mf.Method = method
case 11: // confirm select-ok case 11: // confirm select-ok
//fmt.Println("NextMethod: class:85 method:11") // fmt.Println("NextMethod: class:85 method:11")
method := &confirmSelectOk{} method := &confirmSelectOk{}
if err = method.read(r.r); err != nil { if err = method.read(r.r); err != nil {
return return

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"fmt" "fmt"
@@ -11,6 +11,10 @@ import (
"time" "time"
) )
// DefaultExchange is the default direct exchange that binds every queue by its
// name. Applications can route to a queue using the queue name as routing key.
const DefaultExchange = ""
// Constants for standard AMQP 0-9-1 exchange types. // Constants for standard AMQP 0-9-1 exchange types.
const ( const (
ExchangeDirect = "direct" ExchangeDirect = "direct"
@@ -29,7 +33,7 @@ var (
ErrChannelMax = &Error{Code: ChannelError, Reason: "channel id space exhausted"} ErrChannelMax = &Error{Code: ChannelError, Reason: "channel id space exhausted"}
// ErrSASL is returned from Dial when the authentication mechanism could not // ErrSASL is returned from Dial when the authentication mechanism could not
// be negoated. // be negotiated.
ErrSASL = &Error{Code: AccessRefused, Reason: "SASL could not negotiate a shared mechanism"} ErrSASL = &Error{Code: AccessRefused, Reason: "SASL could not negotiate a shared mechanism"}
// ErrCredentials is returned when the authenticated client is not authorized // ErrCredentials is returned when the authenticated client is not authorized
@@ -61,6 +65,11 @@ var (
ErrFieldType = &Error{Code: SyntaxError, Reason: "unsupported table field type"} ErrFieldType = &Error{Code: SyntaxError, Reason: "unsupported table field type"}
) )
// internal errors used inside the library
var (
errInvalidTypeAssertion = &Error{Code: InternalError, Reason: "type assertion unsuccessful", Server: false, Recover: true}
)
// Error captures the code and reason a channel or connection has been closed // Error captures the code and reason a channel or connection has been closed
// by the server. // by the server.
type Error struct { type Error struct {
@@ -135,6 +144,19 @@ const (
flagReserved1 = 0x0004 flagReserved1 = 0x0004
) )
// Expiration. These constants can be used to set a messages expiration TTL.
// They should be viewed as a clarification of the expiration functionality in
// messages and their usage is not enforced by this pkg.
//
// The server requires a string value that is interpreted by the server as
// milliseconds. If no value is set, which translates to the nil value of
// string, the message will never expire by itself. This does not influence queue
// configured TTL configurations.
const (
NeverExpire string = "" // empty value means never expire
ImmediatelyExpire string = "0" // 0 means immediately expire
)
// Queue captures the current server state of the queue on the server returned // Queue captures the current server state of the queue on the server returned
// from Channel.QueueDeclare or Channel.QueueInspect. // from Channel.QueueDeclare or Channel.QueueInspect.
type Queue struct { type Queue struct {
@@ -153,18 +175,25 @@ type Publishing struct {
Headers Table Headers Table
// Properties // Properties
ContentType string // MIME content type ContentType string // MIME content type
ContentEncoding string // MIME content encoding ContentEncoding string // MIME content encoding
DeliveryMode uint8 // Transient (0 or 1) or Persistent (2) DeliveryMode uint8 // Transient (0 or 1) or Persistent (2)
Priority uint8 // 0 to 9 Priority uint8 // 0 to 9
CorrelationId string // correlation identifier CorrelationId string // correlation identifier
ReplyTo string // address to to reply to (ex: RPC) ReplyTo string // address to to reply to (ex: RPC)
Expiration string // message expiration spec // Expiration represents the message TTL in milliseconds. A value of "0"
MessageId string // message identifier // indicates that the message will immediately expire if the message arrives
Timestamp time.Time // message timestamp // at its destination and the message is not directly handled by a consumer
Type string // message type name // that currently has the capacatity to do so. If you wish the message to
UserId string // creating user id - ex: "guest" // not expire on its own, set this value to any ttl value, empty string or
AppId string // creating application id // use the corresponding constants NeverExpire and ImmediatelyExpire. This
// does not influence queue configured TTL values.
Expiration string
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 // The application specific payload of the message
Body []byte Body []byte
@@ -179,6 +208,16 @@ type Blocking struct {
Reason string // Server reason for activation Reason string // Server reason for activation
} }
// DeferredConfirmation represents a future publisher confirm for a message. It
// allows users to directly correlate a publishing to a confirmation. These are
// returned from PublishWithDeferredConfirm on Channels.
type DeferredConfirmation struct {
DeliveryTag uint64
done chan struct{}
ack bool
}
// Confirmation notifies the acknowledgment or negative acknowledgement of a // Confirmation notifies the acknowledgment or negative acknowledgement of a
// publishing identified by its delivery tag. Use NotifyPublish on the Channel // publishing identified by its delivery tag. Use NotifyPublish on the Channel
// to consume these events. // to consume these events.
@@ -194,23 +233,106 @@ type Decimal struct {
Value int32 Value int32
} }
// Most common queue argument keys in queue declaration. For a comprehensive list
// of queue arguments, visit [RabbitMQ Queue docs].
//
// [QueueTypeArg] queue argument is used to declare quorum and stream queues.
// Accepted values are [QueueTypeClassic] (default), [QueueTypeQuorum] and
// [QueueTypeStream]. [Quorum Queues] accept (almost) all queue arguments as their
// Classic Queues counterparts. Check [feature comparison] docs for more
// information.
//
// Queues can define their [max length] using [QueueMaxLenArg] and
// [QueueMaxLenBytesArg] queue arguments. Overflow behaviour is set using
// [QueueOverflowArg]. Accepted values are [QueueOverflowDropHead] (default),
// [QueueOverflowRejectPublish] and [QueueOverflowRejectPublishDLX].
//
// [Queue TTL] can be defined using [QueueTTLArg]. That is, the time-to-live for an
// unused queue. [Queue Message TTL] can be defined using [QueueMessageTTLArg].
// This will set a time-to-live for messages in the queue.
//
// [Stream retention] can be configured using [StreamMaxLenBytesArg], to set the
// maximum size of the stream. Please note that stream queues always keep, at
// least, one segment. [Stream retention] can also be set using [StreamMaxAgeArg],
// to set time-based retention. Values are string with unit suffix. Valid
// suffixes are Y, M, D, h, m, s. E.g. "7D" for one week. The maximum segment
// size can be set using [StreamMaxSegmentSizeBytesArg]. The default value is
// 500_000_000 bytes ~= 500 megabytes
//
// Starting with RabbitMQ 3.12, consumer timeout can be configured as a queue
// argument. This is the timeout for a consumer to acknowledge a message. The
// value is the time in milliseconds. The timeout is evaluated periodically,
// at one minute intervals. Values lower than one minute are not supported.
// See the [consumer timeout] guide for more information.
//
// [Single Active Consumer] on quorum and classic queues can be configured
// using [SingleActiveConsumerArg]. This argument expects a boolean value. It is
// false by default.
//
// [RabbitMQ Queue docs]: https://rabbitmq.com/queues.html
// [Stream retention]: https://rabbitmq.com/streams.html#retention
// [max length]: https://rabbitmq.com/maxlength.html
// [Queue TTL]: https://rabbitmq.com/ttl.html#queue-ttl
// [Queue Message TTL]: https://rabbitmq.com/ttl.html#per-queue-message-ttl
// [Quorum Queues]: https://rabbitmq.com/quorum-queues.html
// [feature comparison]: https://rabbitmq.com/quorum-queues.html#feature-comparison
// [consumer timeout]: https://rabbitmq.com/consumers.html#acknowledgement-timeout
// [Single Active Consumer]: https://rabbitmq.com/consumers.html#single-active-consumer
const (
QueueTypeArg = "x-queue-type"
QueueMaxLenArg = "x-max-length"
QueueMaxLenBytesArg = "x-max-length-bytes"
StreamMaxLenBytesArg = "x-max-length-bytes"
QueueOverflowArg = "x-overflow"
QueueMessageTTLArg = "x-message-ttl"
QueueTTLArg = "x-expires"
StreamMaxAgeArg = "x-max-age"
StreamMaxSegmentSizeBytesArg = "x-stream-max-segment-size-bytes"
// QueueVersionArg declares the Classic Queue version to use. Expects an integer, either 1 or 2.
QueueVersionArg = "x-queue-version"
// ConsumerTimeoutArg is available in RabbitMQ 3.12+ as a queue argument.
ConsumerTimeoutArg = "x-consumer-timeout"
SingleActiveConsumerArg = "x-single-active-consumer"
)
// Values for queue arguments. Use as values for queue arguments during queue declaration.
// The following argument table will create a classic queue, with max length set to 100 messages,
// and a queue TTL of 30 minutes.
//
// args := amqp.Table{
// amqp.QueueTypeArg: QueueTypeClassic,
// amqp.QueueMaxLenArg: 100,
// amqp.QueueTTLArg: 1800000,
// }
//
// Refer to [Channel.QueueDeclare] for more examples.
const (
QueueTypeClassic = "classic"
QueueTypeQuorum = "quorum"
QueueTypeStream = "stream"
QueueOverflowDropHead = "drop-head"
QueueOverflowRejectPublish = "reject-publish"
QueueOverflowRejectPublishDLX = "reject-publish-dlx"
)
// Table stores user supplied fields of the following types: // Table stores user supplied fields of the following types:
// //
// bool // bool
// byte // byte
// float32 // int8
// float64 // float32
// int // float64
// int16 // int
// int32 // int16
// int64 // int32
// nil // int64
// string // nil
// time.Time // string
// amqp.Decimal // time.Time
// amqp.Table // amqp.Decimal
// []byte // amqp.Table
// []interface{} - containing above types // []byte
// []interface{} - containing above types
// //
// Functions taking a table will immediately fail when the table contains a // Functions taking a table will immediately fail when the table contains a
// value of an unsupported type. // value of an unsupported type.
@@ -221,12 +343,11 @@ type Decimal struct {
// Use a type assertion when reading values from a table for type conversion. // Use a type assertion when reading values from a table for type conversion.
// //
// RabbitMQ expects int32 for integer values. // RabbitMQ expects int32 for integer values.
//
type Table map[string]interface{} type Table map[string]interface{}
func validateField(f interface{}) error { func validateField(f interface{}) error {
switch fv := f.(type) { switch fv := f.(type) {
case nil, bool, byte, int, int16, int32, int64, float32, float64, string, []byte, Decimal, time.Time: case nil, bool, byte, int8, int, int16, int32, int64, float32, float64, string, []byte, Decimal, time.Time:
return nil return nil
case []interface{}: case []interface{}:
@@ -254,17 +375,12 @@ func (t Table) Validate() error {
return validateField(t) return validateField(t)
} }
// Heap interface for maintaining delivery tags // Sets the connection name property. This property can be used in
type tagSet []uint64 // amqp.Config to set a custom connection name during amqp.DialConfig(). This
// can be helpful to identify specific connections in RabbitMQ, for debugging or
func (set tagSet) Len() int { return len(set) } // tracing purposes.
func (set tagSet) Less(i, j int) bool { return (set)[i] < (set)[j] } func (t Table) SetClientConnectionName(connName string) {
func (set tagSet) Swap(i, j int) { (set)[i], (set)[j] = (set)[j], (set)[i] } t["connection_name"] = connName
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 { type message interface {
@@ -288,11 +404,11 @@ The base interface implemented as:
All frames consist of a header (7 octets), a payload of arbitrary size, and a 'frame-end' octet that detects All frames consist of a header (7 octets), a payload of arbitrary size, and a 'frame-end' octet that detects
malformed frames: malformed frames:
0 1 3 7 size+7 size+8 0 1 3 7 size+7 size+8
+------+---------+-------------+ +------------+ +-----------+ +------+---------+-------------+ +------------+ +-----------+
| type | channel | size | | payload | | frame-end | | type | channel | size | | payload | | frame-end |
+------+---------+-------------+ +------------+ +-----------+ +------+---------+-------------+ +------------+ +-----------+
octet short long size octets octet octet short long size octets octet
To read a frame, we: To read a frame, we:
@@ -303,13 +419,24 @@ To read a frame, we:
In realistic implementations where performance is a concern, we would use In realistic implementations where performance is a concern, we would use
read-ahead buffering or gathering reads to avoid doing three separate read-ahead buffering or gathering reads to avoid doing three separate
system calls to read a frame. system calls to read a frame.
*/ */
type frame interface { type frame interface {
write(io.Writer) error write(io.Writer) error
channel() uint16 channel() uint16
} }
/*
Perform any updates on the channel immediately after the frame is decoded while the
connection mutex is held.
*/
func updateChannel(f frame, channel *Channel) {
if mf, isMethodFrame := f.(*methodFrame); isMethodFrame {
if _, isChannelClose := mf.Method.(*channelClose); isChannelClose {
channel.setClosed()
}
}
}
type reader struct { type reader struct {
r io.Reader r io.Reader
} }
@@ -334,17 +461,17 @@ func (protocolHeader) channel() uint16 {
Method frames carry the high-level protocol commands (which we call "methods"). 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: One method frame carries one command. The method frame payload has this format:
0 2 4 0 2 4
+----------+-----------+-------------- - - +----------+-----------+-------------- - -
| class-id | method-id | arguments... | class-id | method-id | arguments...
+----------+-----------+-------------- - - +----------+-----------+-------------- - -
short short ... short short ...
To process a method frame, we: To process a method frame, we:
1. Read the method frame payload. 1. Read the method frame payload.
2. Unpack it into a structure. A given method always has the same structure, 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 so we can unpack the method rapidly. 3. Check that the method is allowed in
the current context. the current context.
4. Check that the method arguments are valid. 4. Check that the method arguments are valid.
5. Execute the method. 5. Execute the method.
@@ -383,11 +510,11 @@ follows it with a content header and zero or more content body frames.
A content header frame has this format: A content header frame has this format:
0 2 4 12 14 0 2 4 12 14
+----------+--------+-----------+----------------+------------- - - +----------+--------+-----------+----------------+------------- - -
| class-id | weight | body size | property flags | property list... | class-id | weight | body size | property flags | property list...
+----------+--------+-----------+----------------+------------- - - +----------+--------+-----------+----------------+------------- - -
short short long long short remainder... short short long long short remainder...
We place content body in distinct frames (rather than including it in the 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 method) so that AMQP may support "zero copy" techniques in which content is
@@ -415,10 +542,10 @@ 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 Looking at the frames for a specific channel, as they pass on the wire, we
might see something like this: might see something like this:
[method] [method]
[method] [header] [body] [body] [method] [header] [body] [body]
[method] [method]
... ...
*/ */
type bodyFrame struct { type bodyFrame struct {
ChannelId uint16 ChannelId uint16
@@ -426,3 +553,16 @@ type bodyFrame struct {
} }
func (f *bodyFrame) channel() uint16 { return f.ChannelId } func (f *bodyFrame) channel() uint16 { return f.ChannelId }
type heartbeatDuration struct {
value time.Duration
hasValue bool
}
func newHeartbeatDurationFromSeconds(s int) heartbeatDuration {
v := time.Duration(s) * time.Second
return heartbeatDuration{
value: v,
hasValue: true,
}
}

View File

@@ -1,12 +1,13 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"errors" "errors"
"fmt"
"net" "net"
"net/url" "net/url"
"strconv" "strconv"
@@ -32,12 +33,20 @@ var defaultURI = URI{
// URI represents a parsed AMQP URI string. // URI represents a parsed AMQP URI string.
type URI struct { type URI struct {
Scheme string Scheme string
Host string Host string
Port int Port int
Username string Username string
Password string Password string
Vhost string Vhost string
CertFile string // client TLS auth - path to certificate (PEM)
CACertFile string // client TLS auth - path to CA certificate (PEM)
KeyFile string // client TLS auth - path to private key (PEM)
ServerName string // client TLS auth - server name
AuthMechanism []string
Heartbeat heartbeatDuration
ConnectionTimeout int
ChannelMax uint16
} }
// ParseURI attempts to parse the given AMQP URI according to the spec. // ParseURI attempts to parse the given AMQP URI according to the spec.
@@ -45,17 +54,32 @@ type URI struct {
// //
// Default values for the fields are: // Default values for the fields are:
// //
// Scheme: amqp // Scheme: amqp
// Host: localhost // Host: localhost
// Port: 5672 // Port: 5672
// Username: guest // Username: guest
// Password: guest // Password: guest
// Vhost: / // Vhost: /
// //
// Supports TLS query parameters. See https://www.rabbitmq.com/uri-query-parameters.html
//
// certfile: <path/to/client_cert.pem>
// keyfile: <path/to/client_key.pem>
// cacertfile: <path/to/ca.pem>
// server_name_indication: <server name>
// auth_mechanism: <one or more: plain, amqplain, external>
// heartbeat: <seconds (integer)>
// connection_timeout: <milliseconds (integer)>
// channel_max: <max number of channels (integer)>
//
// If cacertfile is not provided, system CA certificates will be used.
// Mutual TLS (client auth) will be enabled only in case keyfile AND certfile provided.
//
// If Config.TLSClientConfig is set, TLS parameters from URI will be ignored.
func ParseURI(uri string) (URI, error) { func ParseURI(uri string) (URI, error) {
builder := defaultURI builder := defaultURI
if strings.Contains(uri, " ") == true { if strings.Contains(uri, " ") {
return builder, errURIWhitespace return builder, errURIWhitespace
} }
@@ -113,6 +137,38 @@ func ParseURI(uri string) (URI, error) {
} }
} }
// see https://www.rabbitmq.com/uri-query-parameters.html
params := u.Query()
builder.CertFile = params.Get("certfile")
builder.KeyFile = params.Get("keyfile")
builder.CACertFile = params.Get("cacertfile")
builder.ServerName = params.Get("server_name_indication")
builder.AuthMechanism = params["auth_mechanism"]
if params.Has("heartbeat") {
value, err := strconv.Atoi(params.Get("heartbeat"))
if err != nil {
return builder, fmt.Errorf("heartbeat is not an integer: %v", err)
}
builder.Heartbeat = newHeartbeatDurationFromSeconds(value)
}
if params.Has("connection_timeout") {
value, err := strconv.Atoi(params.Get("connection_timeout"))
if err != nil {
return builder, fmt.Errorf("connection_timeout is not an integer: %v", err)
}
builder.ConnectionTimeout = value
}
if params.Has("channel_max") {
value, err := strconv.ParseUint(params.Get("channel_max"), 10, 16)
if err != nil {
return builder, fmt.Errorf("connection_timeout is not an integer: %v", err)
}
builder.ChannelMax = uint16(value)
}
return builder, nil return builder, nil
} }
@@ -150,8 +206,6 @@ func (uri URI) String() string {
} }
} }
authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port))
if defaultPort, found := schemePorts[uri.Scheme]; !found || defaultPort != uri.Port { if defaultPort, found := schemePorts[uri.Scheme]; !found || defaultPort != uri.Port {
authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port)) authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port))
} else { } else {
@@ -172,5 +226,29 @@ func (uri URI) String() string {
authority.Path = "/" authority.Path = "/"
} }
if uri.CertFile != "" || uri.KeyFile != "" || uri.CACertFile != "" || uri.ServerName != "" {
rawQuery := strings.Builder{}
if uri.CertFile != "" {
rawQuery.WriteString("certfile=")
rawQuery.WriteString(uri.CertFile)
rawQuery.WriteRune('&')
}
if uri.KeyFile != "" {
rawQuery.WriteString("keyfile=")
rawQuery.WriteString(uri.KeyFile)
rawQuery.WriteRune('&')
}
if uri.CACertFile != "" {
rawQuery.WriteString("cacertfile=")
rawQuery.WriteString(uri.CACertFile)
rawQuery.WriteRune('&')
}
if uri.ServerName != "" {
rawQuery.WriteString("server_name_indication=")
rawQuery.WriteString(uri.ServerName)
}
authority.RawQuery = rawQuery.String()
}
return authority.String() return authority.String()
} }

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. // Copyright (c) 2021 VMware, Inc. or its affiliates. All Rights Reserved.
// Copyright (c) 2012-2021, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp package amqp091
import ( import (
"bufio" "bufio"
@@ -15,6 +15,11 @@ import (
"time" "time"
) )
func (w *writer) WriteFrameNoFlush(frame frame) (err error) {
err = frame.write(w.w)
return
}
func (w *writer) WriteFrame(frame frame) (err error) { func (w *writer) WriteFrame(frame frame) (err error) {
if err = frame.write(w.w); err != nil { if err = frame.write(w.w); err != nil {
return return
@@ -63,11 +68,10 @@ func (f *heartbeatFrame) write(w io.Writer) (err error) {
// +----------+--------+-----------+----------------+------------- - - // +----------+--------+-----------+----------------+------------- - -
// | class-id | weight | body size | property flags | property list... // | class-id | weight | body size | property flags | property list...
// +----------+--------+-----------+----------------+------------- - - // +----------+--------+-----------+----------------+------------- - -
// short short long long short remainder...
// //
// short short long long short remainder...
func (f *headerFrame) write(w io.Writer) (err error) { func (f *headerFrame) write(w io.Writer) (err error) {
var payload bytes.Buffer var payload bytes.Buffer
var zeroTime time.Time
if err = binary.Write(&payload, binary.BigEndian, f.ClassId); err != nil { if err = binary.Write(&payload, binary.BigEndian, f.ClassId); err != nil {
return return
@@ -113,7 +117,7 @@ func (f *headerFrame) write(w io.Writer) (err error) {
if len(f.Properties.MessageId) > 0 { if len(f.Properties.MessageId) > 0 {
mask = mask | flagMessageId mask = mask | flagMessageId
} }
if f.Properties.Timestamp != zeroTime { if !f.Properties.Timestamp.IsZero() {
mask = mask | flagTimestamp mask = mask | flagTimestamp
} }
if len(f.Properties.Type) > 0 { if len(f.Properties.Type) > 0 {
@@ -212,7 +216,7 @@ func writeFrame(w io.Writer, typ uint8, channel uint16, payload []byte) (err err
size := uint(len(payload)) size := uint(len(payload))
_, err = w.Write([]byte{ _, err = w.Write([]byte{
byte(typ), typ,
byte((channel & 0xff00) >> 8), byte((channel & 0xff00) >> 8),
byte((channel & 0x00ff) >> 0), byte((channel & 0x00ff) >> 0),
byte((size & 0xff000000) >> 24), byte((size & 0xff000000) >> 24),
@@ -276,7 +280,8 @@ func writeLongstr(w io.Writer, s string) (err error) {
'S': string 'S': string
'T': time.Time 'T': time.Time
'V': nil 'V': nil
'b': byte 'b': int8
'B': byte
'd': float64 'd': float64
'f': float32 'f': float32
'l': int64 'l': int64
@@ -299,8 +304,13 @@ func writeField(w io.Writer, value interface{}) (err error) {
enc = buf[:2] enc = buf[:2]
case byte: case byte:
buf[0] = 'B'
buf[1] = v
enc = buf[:2]
case int8:
buf[0] = 'b' buf[0] = 'b'
buf[1] = byte(v) buf[1] = uint8(v)
enc = buf[:2] enc = buf[:2]
case int16: case int16:
@@ -335,7 +345,7 @@ func writeField(w io.Writer, value interface{}) (err error) {
case Decimal: case Decimal:
buf[0] = 'D' buf[0] = 'D'
buf[1] = byte(v.Scale) buf[1] = v.Scale
binary.BigEndian.PutUint32(buf[2:6], uint32(v.Value)) binary.BigEndian.PutUint32(buf[2:6], uint32(v.Value))
enc = buf[:6] enc = buf[:6]
@@ -412,5 +422,5 @@ func writeTable(w io.Writer, table Table) (err error) {
} }
} }
return writeLongstr(w, string(buf.Bytes())) return writeLongstr(w, buf.String())
} }

View File

@@ -1,12 +0,0 @@
certs/*
spec/spec
examples/simple-consumer/simple-consumer
examples/simple-producer/simple-producer
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
.idea/**/contentModel.xml

View File

@@ -1,25 +0,0 @@
language: go
go:
- 1.10.x
- 1.11.x
- 1.12.x
- 1.13.x
addons:
apt:
packages:
- rabbitmq-server
services:
- rabbitmq
env:
- GO111MODULE=on AMQP_URL=amqp://guest:guest@127.0.0.1:5672/
before_install:
- go get -v golang.org/x/lint/golint
script:
- ./pre-commit
- go test -cpu=1,2 -v -tags integration ./...

View File

@@ -1,35 +0,0 @@
## 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.

View File

@@ -1,100 +0,0 @@
[![Build Status](https://api.travis-ci.org/streadway/amqp.svg)](http://travis-ci.org/streadway/amqp) [![GoDoc](https://godoc.org/github.com/streadway/amqp?status.svg)](http://godoc.org/github.com/streadway/amqp)
# Go RabbitMQ Client Library (Unmaintained Fork)
## Beware of Abandonware
This repository is **NOT ACTIVELY MAINTAINED**. Consider using
a different fork instead: [rabbitmq/amqp091-go](https://github.com/rabbitmq/amqp091-go).
In case of questions, start a discussion in that repo or [use other RabbitMQ community resources](https://rabbitmq.com/contact.html).
## Project Maturity
This project has been used in production systems for many years. As of 2022,
this repository is **NOT ACTIVELY MAINTAINED**.
This repository is **very strict** about any potential public API changes.
You may want to consider [rabbitmq/amqp091-go](https://github.com/rabbitmq/amqp091-go) which
is more willing to adapt the API.
## Supported Go Versions
This library supports two most recent Go release series, currently 1.10 and 1.11.
## 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.
## 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.

View File

@@ -1,94 +0,0 @@
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
}

View File

@@ -1,108 +0,0 @@
// 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 of 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

View File

@@ -1,17 +0,0 @@
// +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
}

View File

@@ -1,67 +0,0 @@
#!/bin/sh
LATEST_STABLE_SUPPORTED_GO_VERSION="1.11"
main() {
if local_go_version_is_latest_stable
then
run_gofmt
run_golint
run_govet
fi
run_unit_tests
}
local_go_version_is_latest_stable() {
go version | grep -q $LATEST_STABLE_SUPPORTED_GO_VERSION
}
log_error() {
echo "$*" 1>&2
}
run_gofmt() {
GOFMT_FILES=$(gofmt -l .)
if [ -n "$GOFMT_FILES" ]
then
log_error "gofmt failed for the following files:
$GOFMT_FILES
please run 'gofmt -w .' on your changes before committing."
exit 1
fi
}
run_golint() {
GOLINT_ERRORS=$(golint ./... | grep -v "Id should be")
if [ -n "$GOLINT_ERRORS" ]
then
log_error "golint failed for the following reasons:
$GOLINT_ERRORS
please run 'golint ./...' on your changes before committing."
exit 1
fi
}
run_govet() {
GOVET_ERRORS=$(go tool vet ./*.go 2>&1)
if [ -n "$GOVET_ERRORS" ]
then
log_error "go vet failed for the following reasons:
$GOVET_ERRORS
please run 'go tool vet ./*.go' on your changes before committing."
exit 1
fi
}
run_unit_tests() {
if [ -z "$NOTEST" ]
then
log_error 'Running short tests...'
env AMQP_URL= go test -short
fi
}
main

6
vendor/modules.txt vendored
View File

@@ -401,6 +401,9 @@ github.com/pkg/profile
# github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 # github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
## explicit ## explicit
github.com/pmezard/go-difflib/difflib github.com/pmezard/go-difflib/difflib
# github.com/rabbitmq/amqp091-go v1.10.0
## explicit; go 1.20
github.com/rabbitmq/amqp091-go
# github.com/rivo/uniseg v0.4.7 # github.com/rivo/uniseg v0.4.7
## explicit; go 1.18 ## explicit; go 1.18
github.com/rivo/uniseg github.com/rivo/uniseg
@@ -422,9 +425,6 @@ github.com/spf13/pflag
# github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf # github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf
## explicit ## explicit
github.com/ssor/bom github.com/ssor/bom
# github.com/streadway/amqp v1.1.0
## explicit; go 1.10
github.com/streadway/amqp
# github.com/stretchr/testify v1.9.0 # github.com/stretchr/testify v1.9.0
## explicit; go 1.17 ## explicit; go 1.17
github.com/stretchr/testify/assert github.com/stretchr/testify/assert