Documentation
¶
Overview ¶
Package azureservicebus provides a local, in-memory emulation of Azure Service Bus's Brokered Messaging REST API: queue and topic/subscription CRUD, and the full send/peek-lock/complete/abandon/dead-letter message lifecycle over HTTP+SharedAccessSignature auth. It deliberately does NOT implement AMQP 1.0 -- sessions and full AMQP compatibility are out of scope for this MVP. See AZURE.md section 9 (M5) and PARITY.md for scope and known gaps.
Index ¶
- Constants
- Variables
- func SignSAS(resource string, expiry int64, keyValue string) string
- func VerifySAS(auth Authorization, keyValue string, now time.Time) bool
- type Authorization
- type ConfigProvider
- type EntityConfig
- type EntityRef
- type Handler
- func (h *Handler) ExtractOperation(c *echo.Context) string
- func (h *Handler) ExtractResource(c *echo.Context) string
- func (h *Handler) GetSupportedOperations() []string
- func (h *Handler) Handler() echo.HandlerFunc
- func (h *Handler) MatchPriority() int
- func (h *Handler) Name() string
- func (h *Handler) Reset()
- func (h *Handler) Restore(ctx context.Context, data []byte) error
- func (h *Handler) RouteMatcher() service.Matcher
- func (h *Handler) Shutdown(ctx context.Context)
- func (h *Handler) Snapshot(ctx context.Context) []byte
- func (h *Handler) StartWorker(ctx context.Context) error
- func (h *Handler) WithJanitor(interval time.Duration) *Handler
- func (h *Handler) WithSASValidation(key string) *Handler
- type InMemoryBackend
- func (b *InMemoryBackend) Abandon(ref EntityRef, deadLetter bool, messageID, lockToken string) error
- func (b *InMemoryBackend) Complete(ref EntityRef, deadLetter bool, messageID, lockToken string) error
- func (b *InMemoryBackend) CreateQueue(name string, cfg ...EntityConfig) (bool, error)
- func (b *InMemoryBackend) CreateSubscription(topic, name string, cfg ...EntityConfig) (bool, error)
- func (b *InMemoryBackend) CreateTopic(name string, cfg ...EntityConfig) (bool, error)
- func (b *InMemoryBackend) DeleteQueue(name string) error
- func (b *InMemoryBackend) DeleteSubscription(topic, name string) error
- func (b *InMemoryBackend) DeleteTopic(name string) error
- func (b *InMemoryBackend) GetQueueInfo(name string) (QueueInfo, error)
- func (b *InMemoryBackend) GetSubscriptionInfo(topic, name string) (SubscriptionInfo, error)
- func (b *InMemoryBackend) GetTopicInfo(name string) (TopicInfo, error)
- func (b *InMemoryBackend) ListQueues() []QueueInfo
- func (b *InMemoryBackend) ListSubscriptions(topic string) ([]SubscriptionInfo, error)
- func (b *InMemoryBackend) ListTopics() []TopicInfo
- func (b *InMemoryBackend) PeekLock(ref EntityRef, deadLetter bool, lockDuration time.Duration) (MessageInfo, error)
- func (b *InMemoryBackend) PeekLockWait(ctx context.Context, ref EntityRef, deadLetter bool, ...) (MessageInfo, error)
- func (b *InMemoryBackend) QueueExists(name string) bool
- func (b *InMemoryBackend) Reset()
- func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error
- func (b *InMemoryBackend) Send(ref EntityRef, msg NewMessage) (MessageInfo, error)
- func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte
- func (b *InMemoryBackend) SubscriptionExists(topic, name string) bool
- func (b *InMemoryBackend) TopicExists(name string) bool
- type Janitor
- type MessageInfo
- type NewMessage
- type Provider
- type QueueInfo
- type Settings
- type StorageBackend
- type SubscriptionInfo
- type TopicInfo
Constants ¶
const ( // DefaultLockDuration is applied to a PeekLock when the caller specifies // no timeout query parameter. DefaultLockDuration = 60 * time.Second // DefaultMessageTTL is applied to Send when the caller supplies no // TimeToLive in BrokerProperties. DefaultMessageTTL = 14 * 24 * time.Hour // MaxDeliveryCount is how many times a message may be // peek-locked-and-abandoned before it is automatically moved to its // entity's dead-letter sub-queue, matching real Service Bus's own // default MaxDeliveryCount. MaxDeliveryCount = 10 // MaxLockDuration is real Service Bus's documented maximum LockDuration // for a queue or subscription (its default is 1 minute; 5 minutes is the // upper bound a CreateQueue/CreateSubscription request may configure -- // see https://learn.microsoft.com/en-us/azure/service-bus-messaging/message-transfers-locks-settlement#peeklock, // which documents "the maximum value is 5 minutes"). handler.go's // validateEntityConfig rejects a create request specifying more than // this with 400 Bad Request, matching real Service Bus's own behavior. MaxLockDuration = 5 * time.Minute )
Default and bound values, matching real Service Bus's documented defaults.
const DefaultKeyName = "RootManageSharedAccessKey"
DefaultKeyName is gopherstack's fixed root shared-access-policy key name, mirroring the real Service Bus default policy name created on every new namespace.
const DefaultKeyValue = "2R1W2VORtFi9HrmRQ1Gxp7xbySq7W0FAs2BvTZDdXeo="
DefaultKeyValue is gopherstack's fixed, publicly published development SAS key (base64-encoded), analogous to pkgs/azureauth.DefaultAccountKey / Azurite's devstoreaccount1 key: a fixed dev secret so azure-sdk-for-go's default connection-string shape works unmodified out of the box, and so WithSASValidation has something deterministic to check against by default.
const DefaultNamespace = "sbemulatorns"
DefaultNamespace is gopherstack's fixed, Azurite-style default Service Bus namespace name. Real Service Bus namespaces are addressed as "<namespace>.servicebus.windows.net"; gopherstack has no DNS story for that, so this constant exists purely so a connection string built from it (Endpoint=sb://<DefaultNamespace>.servicebus.windows.net/;SharedAccessKeyName=...) has a plausible, stable shape for documentation/examples. The actual listener is addressed by host:port (see settings.go's DefaultPort), not by this name.
const DefaultPort = 10003
DefaultPort is Azure Service Bus's fixed, gopherstack-chosen TCP port. This follows the same pattern as services/azureblob's DefaultPort (10000), services/azurequeue's DefaultPort (10001), and services/azuretable's DefaultPort (10002): pick one default and try to bind exactly that, rather than drawing from cli.go's shared --port-range-start/--port-range-end PortAlloc pool. Unlike those three, there is no Azurite Service Bus emulator to mirror -- Azurite doesn't implement Service Bus -- so 10003 is simply the next available slot after Table's 10002 in gopherstack's own numbering convention. See AZURE.md section 9 (M5) for the full rationale.
const MaxPeekLockWaitTimeout = 30 * time.Second
MaxPeekLockWaitTimeout is the ceiling PeekLock's long-poll "?timeout=" query parameter is clamped to. Real Service Bus documents 30 seconds as its own maximum long-poll/operation timeout, and gopherstack matches that value here as a deliberate cap on server-side resource use per in-flight long-poll request (one goroutine and one open connection held for up to this long). The wait is bounded by this cap plus request-context cancellation on client disconnect -- NOT by any http.Server timeout: azureServiceBusReadTimeout only bounds reading the request itself (headers/body), not handler execution time, and this server sets no WriteTimeout or handler deadline, so a long-poll handler blocking well past 60s would not be torn down by the server. See PARITY.md.
Variables ¶
var ( ErrQueueNotFound = errors.New("azureservicebus: queue not found") ErrQueueAlreadyExists = errors.New("azureservicebus: queue already exists") ErrTopicNotFound = errors.New("azureservicebus: topic not found") ErrTopicAlreadyExists = errors.New("azureservicebus: topic already exists") ErrSubscriptionNotFound = errors.New("azureservicebus: subscription not found") ErrSubscriptionExists = errors.New("azureservicebus: subscription already exists") ErrMessageNotFound = errors.New("azureservicebus: message not found") ErrLockTokenMismatch = errors.New("azureservicebus: lock token mismatch") ErrMessageNotLocked = errors.New("azureservicebus: message is not locked") ErrInvalidEntityRef = errors.New("azureservicebus: invalid entity reference") ErrInvalidEntityConfig = errors.New("azureservicebus: invalid entity configuration") // ErrSnapshotNull* are returned by Restore when a snapshot map/slice holds // a JSON null entry, which decodes to a nil pointer that would panic on // first dereference if stored as-is. Mirrors services/azurequeue's // identical family of snapshot-validation errors. ErrSnapshotQueueNull = errors.New("azureservicebus: restore snapshot: queue is null") ErrSnapshotTopicNull = errors.New("azureservicebus: restore snapshot: topic is null") ErrSnapshotSubscriptionNull = errors.New("azureservicebus: restore snapshot: subscription is null") ErrSnapshotMessageNull = errors.New("azureservicebus: restore snapshot: message is null") )
Sentinel errors for Azure Service Bus operations.
var ErrBadPath = errors.New("azureservicebus: unrecognized path")
ErrBadPath is returned by parseRequestPath for a path that doesn't match any recognized Service Bus REST shape.
var ErrInvalidISO8601Duration = errors.New("azureservicebus: invalid ISO 8601 duration")
ErrInvalidISO8601Duration is returned by parseISO8601Duration for a string that doesn't match the supported PnDTnHnMnS/PTnHnMnS subset.
var ErrNilAppContext = errors.New("azureservicebus: nil app context")
ErrNilAppContext is returned when Init is called with a nil AppContext.
Functions ¶
func SignSAS ¶
SignSAS computes the Service Bus SAS signature for resource+expiry using keyValue (base64-encoded), matching the real algorithm: HMAC-SHA256 over "<url-encoded resource>\n<expiry>", base64-encoded.
func VerifySAS ¶
func VerifySAS(auth Authorization, keyValue string, now time.Time) bool
VerifySAS cryptographically verifies auth against keyValue, additionally rejecting an expired token (se in the past relative to now). Callers only invoke this when opting into validation (see Handler.checkAuth / WithSASValidation) -- structural parsing always accepts the header regardless of what this returns.
Types ¶
type Authorization ¶
type Authorization struct {
Resource string // sr: URL-encoded resource URI the token authorizes
Signature string // sig: base64 HMAC-SHA256
KeyName string // skn: shared-access-policy key name
Expiry int64 // se: unix seconds the token expires at
}
Authorization holds the structurally-parsed fields of a Service Bus "Authorization: SharedAccessSignature sr=<resource>&sig=<hmac>&se=<expiry>&skn=<keyname>" header. Parsing never fails on a bad/missing signature -- only on a header that isn't shaped like a SAS token at all -- mirroring pkgs/azureauth's "structural parse always succeeds, verification is opt-in" philosophy (see services/s3's PresignSecret and Blob/Queue/Table's WithSharedKeyValidation).
func ParseSASAuthorization ¶
func ParseSASAuthorization(header string) (Authorization, bool)
ParseSASAuthorization structurally parses a Service Bus SAS Authorization header value. It always extracts whatever key=value pairs are present; ok is false only when the header is missing the scheme prefix entirely.
type ConfigProvider ¶
type ConfigProvider interface {
GetAzureServiceBusSettings() Settings
}
ConfigProvider is a private interface to extract AzureServiceBus configuration from the abstract AppContext Config, mirroring services/azurequeue.ConfigProvider.
type EntityConfig ¶
type EntityConfig struct {
LockDuration time.Duration
DefaultMessageTTL time.Duration
MaxDeliveryCount int
}
EntityConfig holds the per-entity configuration properties real Service Bus accepts on CreateQueue/CreateTopic/CreateSubscription (LockDuration, MaxDeliveryCount, DefaultMessageTimeToLive), parsed from the Atom+XML create-request body (see atom.go). Which fields are meaningful depends on the entity kind: a queue honors all three; a topic honors only DefaultMessageTTL (applied as the message-TTL cap at Send time, since a topic never itself holds messages); a subscription honors LockDuration and MaxDeliveryCount. A zero-valued field falls back to the corresponding package-level default (DefaultLockDuration/MaxDeliveryCount/ DefaultMessageTTL) -- see the lockDuration/maxDeliveryCount/ defaultMessageTTL accessor methods.
type EntityRef ¶
EntityRef identifies a brokered-messaging entity that can hold messages: either a queue, or one specific subscription of a topic (a topic itself never holds messages directly -- sending to a topic fans the message out to every one of its subscriptions' own message lists). Exactly one of Queue or (Topic, Subscription) is set.
type Handler ¶
type Handler struct {
Backend StorageBackend
// Endpoint is e.g. "http://127.0.0.1:10003".
Endpoint string
// SASKeyValue is the base64 key WithSASValidation checks signatures
// against. Defaults to DefaultKeyValue when empty.
SASKeyValue string
// Port is the TCP port StartWorker binds. Set from Settings at Init time
// (see provider.go); defaults to DefaultPort. Single fixed,
// protocol-conventional port -- no fallback pool.
Port int
// ValidateSAS opts checkAuth into cryptographic SAS verification. See
// WithSASValidation.
ValidateSAS bool
// contains filtered or unexported fields
}
Handler is the Echo HTTP handler for Azure Service Bus operations.
func NewHandler ¶
func NewHandler(backend StorageBackend) *Handler
NewHandler creates a new Azure Service Bus Handler. Port defaults to DefaultPort; callers (typically provider.go) override it from Settings.
func (*Handler) ExtractOperation ¶
ExtractOperation extracts the Azure Service Bus operation name from the request, for metrics labeling.
func (*Handler) ExtractResource ¶
ExtractResource extracts the entity resource identifier from the request path, for metrics labeling.
func (*Handler) GetSupportedOperations ¶
GetSupportedOperations returns the list of supported Azure Service Bus operations.
func (*Handler) Handler ¶
func (h *Handler) Handler() echo.HandlerFunc
Handler returns the Echo handler function for Azure Service Bus operations.
func (*Handler) MatchPriority ¶
MatchPriority returns the routing priority for the AzureServiceBus handler. Irrelevant in practice since RouteMatcher never matches.
func (*Handler) Reset ¶
func (h *Handler) Reset()
Reset clears all in-memory state. It is used by the POST /_gopherstack/reset endpoint for CI pipelines and rapid local development.
func (*Handler) RouteMatcher ¶
RouteMatcher exists only to satisfy service.Registerable's interface contract: AzureServiceBus never matches on the shared AWS single-port Router (see Provider's doc comment).
func (*Handler) Shutdown ¶
Shutdown stops the dedicated Service Bus listener. Mirrors services/azurequeue's Shutdown.
func (*Handler) Snapshot ¶
Snapshot implements persistence.Persistable by delegating to the backend.
func (*Handler) StartWorker ¶
StartWorker binds the dedicated Service Bus listener, starts serving on it, and -- if WithJanitor was called -- starts the background lock-expiry/dead-letter sweep. Mirrors services/azurequeue's StartWorker exactly.
func (*Handler) WithJanitor ¶
WithJanitor attaches a background lock-expiry/dead-letter janitor to the handler, mirroring services/azurequeue's WithJanitor. If Backend is not an *InMemoryBackend the call is a no-op.
func (*Handler) WithSASValidation ¶
WithSASValidation enables cryptographic verification of SAS (SharedAccessSignature) Authorization headers, checking each signature against the given key (base64-encoded). A blank key defaults to DefaultKeyValue. Mirrors services/s3's WithPresignValidation and Blob/Queue/Table's WithSharedKeyValidation: when never called, SAS headers are parsed structurally only, never cryptographically rejected.
type InMemoryBackend ¶
type InMemoryBackend struct {
// contains filtered or unexported fields
}
InMemoryBackend implements StorageBackend using in-memory maps guarded by a single RWMutex. Shaped after services/azurequeue's InMemoryBackend.
func NewInMemoryBackend ¶
func NewInMemoryBackend() *InMemoryBackend
NewInMemoryBackend creates a new empty InMemoryBackend.
func (*InMemoryBackend) Abandon ¶
func (b *InMemoryBackend) Abandon(ref EntityRef, deadLetter bool, messageID, lockToken string) error
Abandon releases a locked message's lock (making it immediately available again), after verifying lockToken matches. If DeliveryCount has reached ref's configured MaxDeliveryCount, the message is moved to the entity's dead-letter sub-queue instead of being made available, matching real Service Bus's automatic dead-lettering on delivery-count exhaustion. Either outcome -- released back to the live list, or moved to the dead-letter sub-queue -- makes a message newly visible somewhere on ref, so both wake any PeekLockWait waiter on ref (the same notify channel is shared by the live and dead-letter lists; a waiter on the list that didn't change simply re-checks and goes back to sleep, a harmless spurious wakeup).
func (*InMemoryBackend) Complete ¶
func (b *InMemoryBackend) Complete(ref EntityRef, deadLetter bool, messageID, lockToken string) error
Complete permanently removes a locked message identified by messageID from ref (or its dead-letter sub-queue), after verifying lockToken matches.
func (*InMemoryBackend) CreateQueue ¶
func (b *InMemoryBackend) CreateQueue(name string, cfg ...EntityConfig) (bool, error)
CreateQueue creates a new, empty queue with the given configuration (see EntityConfig; cfg is variadic so pre-existing callers passing only a name keep compiling -- see the StorageBackend interface's doc comment). If a queue with the same name already exists, created is false, err is nil, and its existing configuration is left untouched (idempotent, mirroring services/azurequeue's CreateQueue). ErrQueueAlreadyExists is reserved for a future strict-create variant.
func (*InMemoryBackend) CreateSubscription ¶
func (b *InMemoryBackend) CreateSubscription(topic, name string, cfg ...EntityConfig) (bool, error)
CreateSubscription creates a new, empty subscription of topic with the given configuration (see EntityConfig -- LockDuration and MaxDeliveryCount are meaningful for a subscription; cfg is variadic so pre-existing callers passing only topic/name keep compiling). If a subscription with the same name already exists on that topic, created is false and err is nil (idempotent, mirroring CreateQueue/CreateTopic). Returns ErrTopicNotFound if the topic does not exist.
Real Service Bus subscription creation accepts an optional SQL filter rule in the request body; this MVP deliberately does not parse or store it -- every subscription behaves as if it had Service Bus's default "match all" rule (TrueFilter). See PARITY.md's filter-evaluation gap and handler.go's createSubscription, which discards the request body's filter rule after extracting any EntityConfig properties it also carries.
func (*InMemoryBackend) CreateTopic ¶
func (b *InMemoryBackend) CreateTopic(name string, cfg ...EntityConfig) (bool, error)
CreateTopic creates a new, empty topic (no subscriptions) with the given configuration (see EntityConfig -- only DefaultMessageTTL is meaningful for a topic; cfg is variadic so pre-existing callers passing only a name keep compiling). If a topic with the same name already exists, created is false and err is nil, mirroring CreateQueue's idempotent-retry semantics.
func (*InMemoryBackend) DeleteQueue ¶
func (b *InMemoryBackend) DeleteQueue(name string) error
DeleteQueue removes a queue and all of its messages (including any dead-lettered ones). Returns ErrQueueNotFound if the queue does not exist.
func (*InMemoryBackend) DeleteSubscription ¶
func (b *InMemoryBackend) DeleteSubscription(topic, name string) error
DeleteSubscription removes a subscription and all of its messages (including any dead-lettered ones). Returns ErrTopicNotFound or ErrSubscriptionNotFound as appropriate.
func (*InMemoryBackend) DeleteTopic ¶
func (b *InMemoryBackend) DeleteTopic(name string) error
DeleteTopic removes a topic, all of its subscriptions, and every message held by those subscriptions. Returns ErrTopicNotFound if the topic does not exist.
func (*InMemoryBackend) GetQueueInfo ¶
func (b *InMemoryBackend) GetQueueInfo(name string) (QueueInfo, error)
GetQueueInfo returns name's metadata. Returns ErrQueueNotFound if it does not exist.
func (*InMemoryBackend) GetSubscriptionInfo ¶
func (b *InMemoryBackend) GetSubscriptionInfo(topic, name string) (SubscriptionInfo, error)
GetSubscriptionInfo returns name's metadata within topic. Returns ErrTopicNotFound or ErrSubscriptionNotFound as appropriate.
func (*InMemoryBackend) GetTopicInfo ¶
func (b *InMemoryBackend) GetTopicInfo(name string) (TopicInfo, error)
GetTopicInfo returns name's metadata. Returns ErrTopicNotFound if it does not exist.
func (*InMemoryBackend) ListQueues ¶
func (b *InMemoryBackend) ListQueues() []QueueInfo
ListQueues returns every queue's metadata, sorted by name.
func (*InMemoryBackend) ListSubscriptions ¶
func (b *InMemoryBackend) ListSubscriptions(topic string) ([]SubscriptionInfo, error)
ListSubscriptions returns every subscription of topic's metadata, sorted by name. Returns ErrTopicNotFound if topic does not exist.
func (*InMemoryBackend) ListTopics ¶
func (b *InMemoryBackend) ListTopics() []TopicInfo
ListTopics returns every topic's metadata, sorted by name.
func (*InMemoryBackend) PeekLock ¶
func (b *InMemoryBackend) PeekLock(ref EntityRef, deadLetter bool, lockDuration time.Duration) (MessageInfo, error)
PeekLock returns the oldest visible (unlocked, non-expired) message on ref -- or its dead-letter sub-queue, if deadLetter is true -- locking it for lockDuration and incrementing its delivery count. Returns ErrMessageNotFound if none is available. See the StorageBackend doc comment for lockDuration <= 0's fallback behavior.
func (*InMemoryBackend) PeekLockWait ¶
func (b *InMemoryBackend) PeekLockWait( ctx context.Context, ref EntityRef, deadLetter bool, lockDuration, timeout time.Duration, ) (MessageInfo, error)
PeekLockWait is PeekLock's long-poll variant. See the StorageBackend doc comment. Mirrors services/sqs's ReceiveMessage/pollReceive/receiveOnce long-poll shape (a broadcast notify channel plus a 1-second recheck-timer backstop), adapted to take a context so a disconnecting client releases the waiting goroutine immediately -- a deliberate improvement over the SQS precedent, which has no ctx parameter (see PARITY.md). Deadline/backstop timing uses the real wall clock (time.Now/time.Timer), not the backend's mockable nowFunc -- matching services/sqs's identical choice and making this method exercisable under testing/synctest's fake clock.
func (*InMemoryBackend) QueueExists ¶
func (b *InMemoryBackend) QueueExists(name string) bool
QueueExists reports whether a queue named name exists.
func (*InMemoryBackend) Reset ¶
func (b *InMemoryBackend) Reset()
Reset clears all in-memory state. It is used by the POST /_gopherstack/reset endpoint for CI pipelines and rapid local development.
func (*InMemoryBackend) Restore ¶
func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error
Restore loads backend state from a JSON snapshot. It implements persistence.Persistable.
func (*InMemoryBackend) Send ¶
func (b *InMemoryBackend) Send(ref EntityRef, msg NewMessage) (MessageInfo, error)
Send enqueues msg on ref. See the StorageBackend.Send doc comment for the queue-vs-topic fan-out distinction and the per-entity TTL cap.
func (*InMemoryBackend) Snapshot ¶
func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte
Snapshot serialises the backend state to JSON. It implements persistence.Persistable.
func (*InMemoryBackend) SubscriptionExists ¶
func (b *InMemoryBackend) SubscriptionExists(topic, name string) bool
SubscriptionExists reports whether topic has a subscription named name. Returns false (not an error) if topic itself does not exist.
func (*InMemoryBackend) TopicExists ¶
func (b *InMemoryBackend) TopicExists(name string) bool
TopicExists reports whether a topic named name exists.
type Janitor ¶
type Janitor struct {
Backend *InMemoryBackend
Interval time.Duration
}
Janitor is the Azure Service Bus background worker responsible for the two things real Service Bus does automatically over time: releasing a peek-locked message whose lock has expired without being Completed or Abandoned (making it available for redelivery, or dead-lettering it if that expiry pushed its delivery count past MaxDeliveryCount), and moving a TTL-expired message to its entity's dead-letter sub-queue. Mirrors services/azurequeue's Janitor/TTL-sweep shape.
func NewJanitor ¶
func NewJanitor(backend *InMemoryBackend, interval time.Duration) *Janitor
NewJanitor creates a new Azure Service Bus Janitor for the given backend.
type MessageInfo ¶
type MessageInfo struct {
EnqueuedTime time.Time
LockedUntil time.Time
CustomHeaders map[string]string
ContentType string
Label string
CorrelationID string
MessageID string
ReplyTo string
SessionID string
LockToken string
Body []byte
SequenceNumber int64
DeliveryCount int64
}
MessageInfo is the public, read-only snapshot of a stored message returned by Send/PeekLock. LockToken/LockedUntil are populated only for a peek-locked read; Send returns them zero.
type NewMessage ¶
type NewMessage struct {
CustomHeaders map[string]string
ContentType string
Label string
CorrelationID string
MessageID string
ReplyTo string
SessionID string
Body []byte
TimeToLive time.Duration
}
NewMessage is the input shape for sending a message, built from the incoming request's body and BrokerProperties header (see message_ops.go).
type Provider ¶
type Provider struct{}
Provider implements service.Provider for the Azure Service Bus service.
Like services/azureblob/azurequeue/azuretable, AzureServiceBus does not register a RouteMatcher into the shared AWS single-port Router: its path shape (/<queue-or-topic>[/subscriptions/<name>][/messages[/...]]) has no service-identifying header the way AWS's X-Amz-Target does, and multiplexing it onto a shared port risks exactly the collision the router avoids by construction for AWS services (see AZURE.md section 4). Instead the returned Handler implements service.BackgroundWorker and stands up its own dedicated *echo.Echo/*http.Server, listening on a fixed port (DefaultPort, 10003). It is registered in cli.go's getMostRecentServiceProviders like every other provider; only its RouteMatcher (which always returns false) is inert.
func (*Provider) Init ¶
func (p *Provider) Init(ctx *service.AppContext) (service.Registerable, error)
Init initializes the AzureServiceBus service backend and handler. The configured port (Settings.Port, default DefaultPort) is only recorded here; the actual TCP bind happens synchronously in Handler.StartWorker, so a port-in-use failure is returned to the caller directly instead of being discovered later from a background goroutine.
type QueueInfo ¶
type QueueInfo struct {
CreatedAt time.Time
Name string
LockDuration time.Duration
DefaultMessageTTL time.Duration
MaxDeliveryCount int
}
QueueInfo is the read-only metadata snapshot returned by GetQueueInfo/ ListQueues.
type Settings ¶
type Settings struct {
// Port is the fixed TCP port for the dedicated Service Bus listener. See
// handler.go's StartWorker for what happens when it's unavailable (fails
// fast; no fallback pool, matching services/azureblob/azurequeue/azuretable).
Port int `` //nolint:lll // config struct tags are intentionally verbose
/* 189-byte string literal not displayed */
// ValidateSAS opts the handler into cryptographic verification of SAS
// (SharedAccessSignature) tokens against DevKeyValue (or a caller-supplied
// key via WithSASValidation), mirroring services/s3's
// --validate-sigv4/PresignSecret opt-in pattern and Blob/Queue/Table's
// WithSharedKeyValidation. Off by default: SAS tokens are always
// structurally parsed (key name + resource scope extracted) but not
// cryptographically checked unless this is set.
ValidateSAS bool `` //nolint:lll // config struct tags are intentionally verbose
/* 247-byte string literal not displayed */
}
Settings holds service-level configuration for the Azure Service Bus backend. Fields are picked up by the Kong CLI parser when this struct is embedded in the root CLI command (see cli.go's CLI.AzureServiceBus field), mirroring services/azurequeue's and services/azuretable's Settings pattern.
func DefaultSettings ¶
func DefaultSettings() Settings
DefaultSettings returns the default Settings. Used when no ConfigProvider is available at init time (e.g. tests constructing a Provider directly).
type StorageBackend ¶
type StorageBackend interface {
// CreateQueue creates a queue. cfg is variadic purely so every
// pre-existing call site that only ever passed a name keeps compiling;
// at most cfg[0] is used (see EntityConfig, firstConfig).
CreateQueue(name string, cfg ...EntityConfig) (created bool, err error)
DeleteQueue(name string) error
QueueExists(name string) bool
GetQueueInfo(name string) (QueueInfo, error)
ListQueues() []QueueInfo
// CreateTopic creates a topic. Only cfg's DefaultMessageTTL field is
// meaningful (see EntityConfig's doc comment); see CreateQueue's doc
// comment for why cfg is variadic.
CreateTopic(name string, cfg ...EntityConfig) (created bool, err error)
DeleteTopic(name string) error
TopicExists(name string) bool
GetTopicInfo(name string) (TopicInfo, error)
ListTopics() []TopicInfo
// CreateSubscription creates a subscription of topic. The filter rule
// (if any) is accepted structurally but not stored/evaluated -- every
// rule is treated as match-all (see PARITY.md's filter-evaluation gap).
// Only cfg's LockDuration/MaxDeliveryCount fields are meaningful; see
// CreateQueue's doc comment for why cfg is variadic.
CreateSubscription(topic, name string, cfg ...EntityConfig) (created bool, err error)
DeleteSubscription(topic, name string) error
SubscriptionExists(topic, name string) bool
GetSubscriptionInfo(topic, name string) (SubscriptionInfo, error)
ListSubscriptions(topic string) ([]SubscriptionInfo, error)
// Send enqueues msg on ref. For a queue ref it is appended to that
// queue's own message list. For a topic ref (Topic set, Subscription
// empty) it is fanned out: an independent copy is appended to every one
// of that topic's subscriptions' own message lists, matching real
// Service Bus's one-to-many topic delivery (see AZURE.md section 9's M5
// entry and services/sns's topic/subscription fan-out as the structural
// reference). Returns ErrTopicNotFound if the topic has no
// subscriptions registered as an error -- fan-out to zero subscriptions
// is not an error, matching real Service Bus (the message is simply
// dropped, as no subscription exists to receive it). msg.TimeToLive, if
// set, is capped at the target entity's configured
// DefaultMessageTimeToLive (real Service Bus semantics); if unset, the
// entity's DefaultMessageTimeToLive is used outright. Send also wakes
// any goroutine blocked in PeekLockWait on the affected entity/entities.
Send(ref EntityRef, msg NewMessage) (MessageInfo, error)
// PeekLock performs a destructive read: it returns the oldest visible,
// non-expired message on ref (or its dead-letter sub-queue, if
// deadLetter is true), locking it for lockDuration and incrementing its
// delivery count. lockDuration <= 0 resolves to ref's own configured
// LockDuration (falling back to DefaultLockDuration if ref has none
// configured) rather than a caller-supplied value -- see EntityConfig.
// Returns ErrMessageNotFound if none is available.
PeekLock(ref EntityRef, deadLetter bool, lockDuration time.Duration) (MessageInfo, error)
// PeekLockWait is PeekLock's long-poll variant: if no message is
// immediately visible, it waits up to timeout for one to arrive (a
// message becoming visible via Send, Abandon's release path, or the
// Janitor's lock-release path all wake a waiter), or until ctx is
// cancelled, whichever comes first. timeout <= 0 behaves exactly like an
// immediate PeekLock call. Callers are expected to have already clamped
// timeout to a sane maximum (see handler.go's MaxPeekLockWaitTimeout);
// PeekLockWait itself does not enforce a cap.
PeekLockWait(
ctx context.Context,
ref EntityRef,
deadLetter bool,
lockDuration, timeout time.Duration,
) (MessageInfo, error)
// Complete permanently removes a locked message identified by messageID,
// after verifying lockToken matches. Returns ErrLockTokenMismatch or
// ErrMessageNotFound as appropriate.
Complete(ref EntityRef, deadLetter bool, messageID, lockToken string) error
// Abandon releases a locked message's lock (making it immediately
// available again) after verifying lockToken matches, without altering
// its position. If the message's delivery count has reached ref's
// configured MaxDeliveryCount (see EntityConfig) it is moved to the
// dead-letter sub-queue instead.
Abandon(ref EntityRef, deadLetter bool, messageID, lockToken string) error
// Reset clears all in-memory state. Used by the
// POST /_gopherstack/reset endpoint for CI pipelines and rapid local development.
Reset()
}
StorageBackend defines the interface for an Azure Service Bus backend. Shaped after services/azurequeue's StorageBackend: a narrow, testable seam between the wire handler and storage, so handler tests can substitute a fake.