diff --git a/coolamqp/framing/__init__.py b/coolamqp/framing/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9f2b35b38d89264ee25685611d0a65a192e165f6 --- /dev/null +++ b/coolamqp/framing/__init__.py @@ -0,0 +1,2 @@ +# coding=UTF-8 +from __future__ import absolute_import, division, print_function diff --git a/coolamqp/framing/definitions.py b/coolamqp/framing/definitions.py new file mode 100644 index 0000000000000000000000000000000000000000..921c176dd1371100cb57675772c4585383cdeb23 --- /dev/null +++ b/coolamqp/framing/definitions.py @@ -0,0 +1,2589 @@ +# coding=UTF-8 +from __future__ import print_function, absolute_import +""" +Constants used in AMQP protocol. + +Generated automatically by CoolAMQP from AMQP machine-readable specification. +See utils/compdefs.py for the tool + +AMQP is copyright (c) 2016 OASIS +CoolAMQP is copyright (c) 2016 DMS Serwis s.c. +""" + +# Core frame types +FRAME_METHOD = 1 +FRAME_HEADER = 2 +FRAME_BODY = 3 +FRAME_HEARTBEAT = 8 +FRAME_MIN_SIZE = 4096 +FRAME_END = 206 +REPLY_SUCCESS = 200 # Indicates that the method completed successfully. This reply code is + # reserved for future use - the current protocol design does not use positive + # confirmation and reply codes are sent only in case of an error. +CONTENT_TOO_LARGE = 311 # The client attempted to transfer content larger than the server could accept + # at the present time. The client may retry at a later time. +NO_CONSUMERS = 313 # When the exchange cannot deliver to a consumer when the immediate flag is + # set. As a result of pending data on the queue or the absence of any + # consumers of the queue. +CONNECTION_FORCED = 320 # An operator intervened to close the connection for some reason. The client + # may retry at some later date. +INVALID_PATH = 402 # The client tried to work with an unknown virtual host. +ACCESS_REFUSED = 403 # The client attempted to work with a server entity to which it has no + # access due to security settings. +NOT_FOUND = 404 # The client attempted to work with a server entity that does not exist. +RESOURCE_LOCKED = 405 # The client attempted to work with a server entity to which it has no + # access because another client is working with it. +PRECONDITION_FAILED = 406 # The client requested a method that was not allowed because some precondition + # failed. +FRAME_ERROR = 501 # The sender sent a malformed frame that the recipient could not decode. + # This strongly implies a programming error in the sending peer. +SYNTAX_ERROR = 502 # The sender sent a frame that contained illegal values for one or more + # fields. This strongly implies a programming error in the sending peer. +COMMAND_INVALID = 503 # The client sent an invalid sequence of frames, attempting to perform an + # operation that was considered invalid by the server. This usually implies + # a programming error in the client. +CHANNEL_ERROR = 504 # The client attempted to work with a channel that had not been correctly + # opened. This most likely indicates a fault in the client layer. +UNEXPECTED_FRAME = 505 # The peer sent a frame that was not expected, usually in the context of + # a content header and body. This strongly indicates a fault in the peer's + # content processing. +RESOURCE_ERROR = 506 # The server could not complete the method because it lacked sufficient + # resources. This may be due to the client creating too many of some type + # of entity. +NOT_ALLOWED = 530 # The client tried to work with some entity in a manner that is prohibited + # by the server, due to security settings or by some other criteria. +NOT_IMPLEMENTED = 540 # The client tried to use functionality that is not implemented in the + # server. +INTERNAL_ERROR = 541 # The server could not complete the method because of an internal error. + # The server may require intervention by an operator in order to resume + # normal operations. +DOMAIN_TO_BASIC_TYPE = { + u'class-id': u'short', + u'consumer-tag': u'shortstr', + u'delivery-tag': u'longlong', + u'exchange-name': u'shortstr', + u'method-id': u'short', + u'no-ack': u'bit', + u'no-local': u'bit', + u'no-wait': u'bit', + u'path': u'shortstr', + u'peer-properties': u'table', + u'queue-name': u'shortstr', + u'redelivered': u'bit', + u'message-count': u'long', + u'reply-code': u'short', + u'reply-text': u'shortstr', + u'bit': None, + u'octet': None, + u'short': None, + u'long': None, + u'longlong': None, + u'shortstr': None, + u'longstr': None, + u'timestamp': None, + u'table': None, +} + + +class AMQPClass(object): + pass + + +class AMQPMethod(object): + RESPONSE_TO = None + REPLY_WITH = [] + + +class Connection(AMQPClass): + """ + The connection class provides methods for a client to establish a network connection to + + a server, and for both peers to operate the connection thereafter. + """ + NAME = u'connection' + INDEX = 10 + + +class ConnectionClose(AMQPMethod): + """ + Request a connection close + + This method indicates that the sender wants to close the connection. This may be + due to internal conditions (e.g. a forced shut-down) or due to an error handling + a specific method, i.e. an exception. When a close is due to an exception, the + sender provides the class and method id of the method which caused the exception. + """ + CLASS = Connection + NAME = u'close' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 50 + FULLNAME = u'connection.close' + SYNCHRONOUS = True + REPLY_WITH = [ConnectionCloseOk] + FIELDS = [ + (u'reply-code', u'reply-code', u'short'), + (u'reply-text', u'reply-text', u'shortstr'), + (u'class-id', u'class-id', u'short'), # failing method class + (u'method-id', u'method-id', u'short'), # failing method ID + ] + + def __init__(self, reply_code, reply_text, class_id, method_id): + """ + Create frame connection.close + + :type reply_code: reply-code (as short) + :type reply_text: reply-text (as shortstr) + :param class_id: Failing method class + When the close is provoked by a method exception, this is the class of the + method. + :type class_id: class-id (as short) + :param method_id: Failing method id + When the close is provoked by a method exception, this is the ID of the method. + :type method_id: method-id (as short) + """ + self.reply_code = reply_code + self.reply_text = reply_text + self.class_id = class_id + self.method_id = method_id + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ConnectionCloseOk(AMQPMethod): + """ + Confirm a connection close + + This method confirms a Connection.Close method and tells the recipient that it is + safe to release resources for the connection and close the socket. + """ + CLASS = Connection + NAME = u'close-ok' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 51 + FULLNAME = u'connection.close-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = ConnectionClose + FIELDS = [ + ] + + def __init__(self): + """ + Create frame connection.close-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ConnectionOpen(AMQPMethod): + """ + Open connection to virtual host + + This method opens a connection to a virtual host, which is a collection of + resources, and acts to separate multiple application domains within a server. + The server may apply arbitrary limits per virtual host, such as the number + of each type of entity that may be used, per connection and/or in total. + """ + CLASS = Connection + NAME = u'open' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 40 + FULLNAME = u'connection.open' + SYNCHRONOUS = True + REPLY_WITH = [ConnectionOpenOk] + FIELDS = [ + (u'virtual-host', u'path', u'shortstr'), # virtual host name + (u'reserved-1', u'shortstr', u'shortstr'), + (u'reserved-2', u'bit', u'bit'), + ] + + def __init__(self, virtual_host): + """ + Create frame connection.open + + :param virtual_host: Virtual host name + The name of the virtual host to work with. + :type virtual_host: path (as shortstr) + """ + self.virtual_host = virtual_host + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ConnectionOpenOk(AMQPMethod): + """ + Signal that connection is ready + + This method signals to the client that the connection is ready for use. + """ + CLASS = Connection + NAME = u'open-ok' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 41 + FULLNAME = u'connection.open-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = ConnectionOpen + FIELDS = [ + (u'reserved-1', u'shortstr', u'shortstr'), + ] + + def __init__(self): + """ + Create frame connection.open-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ConnectionStart(AMQPMethod): + """ + Start connection negotiation + + This method starts the connection negotiation process by telling the client the + protocol version that the server proposes, along with a list of security mechanisms + which the client can use for authentication. + """ + CLASS = Connection + NAME = u'start' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 10 + FULLNAME = u'connection.start' + SYNCHRONOUS = True + REPLY_WITH = [ConnectionStartOk] + FIELDS = [ + (u'version-major', u'octet', u'octet'), # protocol major version + (u'version-minor', u'octet', u'octet'), # protocol minor version + (u'server-properties', u'peer-properties', u'table'), # server properties + (u'mechanisms', u'longstr', u'longstr'), # available security mechanisms + (u'locales', u'longstr', u'longstr'), # available message locales + ] + + def __init__(self, version_major, version_minor, server_properties, mechanisms, locales): + """ + Create frame connection.start + + :param version_major: Protocol major version + The major version number can take any value from 0 to 99 as defined in the + AMQP specification. + :type version_major: octet (as octet) + :param version_minor: Protocol minor version + The minor version number can take any value from 0 to 99 as defined in the + AMQP specification. + :type version_minor: octet (as octet) + :param server_properties: Server properties + The properties SHOULD contain at least these fields: "host", specifying the + server host name or address, "product", giving the name of the server product, + "version", giving the name of the server version, "platform", giving the name + of the operating system, "copyright", if appropriate, and "information", giving + other general information. + :type server_properties: peer-properties (as table) + :param mechanisms: Available security mechanisms + A list of the security mechanisms that the server supports, delimited by spaces. + :type mechanisms: longstr (as longstr) + :param locales: Available message locales + A list of the message locales that the server supports, delimited by spaces. The + locale defines the language in which the server will send reply texts. + :type locales: longstr (as longstr) + """ + self.version_major = version_major + self.version_minor = version_minor + self.server_properties = server_properties + self.mechanisms = mechanisms + self.locales = locales + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ConnectionSecure(AMQPMethod): + """ + Security mechanism challenge + + The SASL protocol works by exchanging challenges and responses until both peers have + received sufficient information to authenticate each other. This method challenges + the client to provide more information. + """ + CLASS = Connection + NAME = u'secure' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 20 + FULLNAME = u'connection.secure' + SYNCHRONOUS = True + REPLY_WITH = [ConnectionSecureOk] + FIELDS = [ + (u'challenge', u'longstr', u'longstr'), # security challenge data + ] + + def __init__(self, challenge): + """ + Create frame connection.secure + + :param challenge: Security challenge data + Challenge information, a block of opaque binary data passed to the security + mechanism. + :type challenge: longstr (as longstr) + """ + self.challenge = challenge + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ConnectionStartOk(AMQPMethod): + """ + Select security mechanism and locale + + This method selects a SASL security mechanism. + """ + CLASS = Connection + NAME = u'start-ok' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 11 + FULLNAME = u'connection.start-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = ConnectionStart + FIELDS = [ + (u'client-properties', u'peer-properties', u'table'), # client properties + (u'mechanism', u'shortstr', u'shortstr'), # selected security mechanism + (u'response', u'longstr', u'longstr'), # security response data + (u'locale', u'shortstr', u'shortstr'), # selected message locale + ] + + def __init__(self, client_properties, mechanism, response, locale): + """ + Create frame connection.start-ok + + :param client_properties: Client properties + The properties SHOULD contain at least these fields: "product", giving the name + of the client product, "version", giving the name of the client version, "platform", + giving the name of the operating system, "copyright", if appropriate, and + "information", giving other general information. + :type client_properties: peer-properties (as table) + :param mechanism: Selected security mechanism + A single security mechanisms selected by the client, which must be one of those + specified by the server. + :type mechanism: shortstr (as shortstr) + :param response: Security response data + A block of opaque data passed to the security mechanism. The contents of this + data are defined by the SASL security mechanism. + :type response: longstr (as longstr) + :param locale: Selected message locale + A single message locale selected by the client, which must be one of those + specified by the server. + :type locale: shortstr (as shortstr) + """ + self.client_properties = client_properties + self.mechanism = mechanism + self.response = response + self.locale = locale + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ConnectionSecureOk(AMQPMethod): + """ + Security mechanism response + + This method attempts to authenticate, passing a block of SASL data for the security + mechanism at the server side. + """ + CLASS = Connection + NAME = u'secure-ok' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 21 + FULLNAME = u'connection.secure-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = ConnectionSecure + FIELDS = [ + (u'response', u'longstr', u'longstr'), # security response data + ] + + def __init__(self, response): + """ + Create frame connection.secure-ok + + :param response: Security response data + A block of opaque data passed to the security mechanism. The contents of this + data are defined by the SASL security mechanism. + :type response: longstr (as longstr) + """ + self.response = response + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ConnectionTune(AMQPMethod): + """ + Propose connection tuning parameters + + This method proposes a set of connection configuration values to the client. The + client can accept and/or adjust these. + """ + CLASS = Connection + NAME = u'tune' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 30 + FULLNAME = u'connection.tune' + SYNCHRONOUS = True + REPLY_WITH = [ConnectionTuneOk] + FIELDS = [ + (u'channel-max', u'short', u'short'), # proposed maximum channels + (u'frame-max', u'long', u'long'), # proposed maximum frame size + (u'heartbeat', u'short', u'short'), # desired heartbeat delay + ] + + def __init__(self, channel_max, frame_max, heartbeat): + """ + Create frame connection.tune + + :param channel_max: Proposed maximum channels + Specifies highest channel number that the server permits. Usable channel numbers + are in the range 1..channel-max. Zero indicates no specified limit. + :type channel_max: short (as short) + :param frame_max: Proposed maximum frame size + The largest frame size that the server proposes for the connection, including + frame header and end-byte. The client can negotiate a lower value. Zero means + that the server does not impose any specific limit but may reject very large + frames if it cannot allocate resources for them. + :type frame_max: long (as long) + :param heartbeat: Desired heartbeat delay + The delay, in seconds, of the connection heartbeat that the server wants. + Zero means the server does not want a heartbeat. + :type heartbeat: short (as short) + """ + self.channel_max = channel_max + self.frame_max = frame_max + self.heartbeat = heartbeat + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ConnectionTuneOk(AMQPMethod): + """ + Negotiate connection tuning parameters + + This method sends the client's connection tuning parameters to the server. + Certain fields are negotiated, others provide capability information. + """ + CLASS = Connection + NAME = u'tune-ok' + CLASSNAME = u'connection' + CLASS_INDEX = 10 + METHOD_INDEX = 31 + FULLNAME = u'connection.tune-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = ConnectionTune + FIELDS = [ + (u'channel-max', u'short', u'short'), # negotiated maximum channels + (u'frame-max', u'long', u'long'), # negotiated maximum frame size + (u'heartbeat', u'short', u'short'), # desired heartbeat delay + ] + + def __init__(self, channel_max, frame_max, heartbeat): + """ + Create frame connection.tune-ok + + :param channel_max: Negotiated maximum channels + The maximum total number of channels that the client will use per connection. + :type channel_max: short (as short) + :param frame_max: Negotiated maximum frame size + The largest frame size that the client and server will use for the connection. + Zero means that the client does not impose any specific limit but may reject + very large frames if it cannot allocate resources for them. Note that the + frame-max limit applies principally to content frames, where large contents can + be broken into frames of arbitrary size. + :type frame_max: long (as long) + :param heartbeat: Desired heartbeat delay + The delay, in seconds, of the connection heartbeat that the client wants. Zero + means the client does not want a heartbeat. + :type heartbeat: short (as short) + """ + self.channel_max = channel_max + self.frame_max = frame_max + self.heartbeat = heartbeat + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + +class Channel(AMQPClass): + """ + The channel class provides methods for a client to establish a channel to a + + server and for both peers to operate the channel thereafter. + """ + NAME = u'channel' + INDEX = 20 + + +class ChannelClose(AMQPMethod): + """ + Request a channel close + + This method indicates that the sender wants to close the channel. This may be due to + internal conditions (e.g. a forced shut-down) or due to an error handling a specific + method, i.e. an exception. When a close is due to an exception, the sender provides + the class and method id of the method which caused the exception. + """ + CLASS = Channel + NAME = u'close' + CLASSNAME = u'channel' + CLASS_INDEX = 20 + METHOD_INDEX = 40 + FULLNAME = u'channel.close' + SYNCHRONOUS = True + REPLY_WITH = [ChannelCloseOk] + FIELDS = [ + (u'reply-code', u'reply-code', u'short'), + (u'reply-text', u'reply-text', u'shortstr'), + (u'class-id', u'class-id', u'short'), # failing method class + (u'method-id', u'method-id', u'short'), # failing method ID + ] + + def __init__(self, reply_code, reply_text, class_id, method_id): + """ + Create frame channel.close + + :type reply_code: reply-code (as short) + :type reply_text: reply-text (as shortstr) + :param class_id: Failing method class + When the close is provoked by a method exception, this is the class of the + method. + :type class_id: class-id (as short) + :param method_id: Failing method id + When the close is provoked by a method exception, this is the ID of the method. + :type method_id: method-id (as short) + """ + self.reply_code = reply_code + self.reply_text = reply_text + self.class_id = class_id + self.method_id = method_id + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ChannelCloseOk(AMQPMethod): + """ + Confirm a channel close + + This method confirms a Channel.Close method and tells the recipient that it is safe + to release resources for the channel. + """ + CLASS = Channel + NAME = u'close-ok' + CLASSNAME = u'channel' + CLASS_INDEX = 20 + METHOD_INDEX = 41 + FULLNAME = u'channel.close-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = ChannelClose + FIELDS = [ + ] + + def __init__(self): + """ + Create frame channel.close-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ChannelFlow(AMQPMethod): + """ + Enable/disable flow from peer + + This method asks the peer to pause or restart the flow of content data sent by + a consumer. This is a simple flow-control mechanism that a peer can use to avoid + overflowing its queues or otherwise finding itself receiving more messages than + it can process. Note that this method is not intended for window control. It does + not affect contents returned by Basic.Get-Ok methods. + """ + CLASS = Channel + NAME = u'flow' + CLASSNAME = u'channel' + CLASS_INDEX = 20 + METHOD_INDEX = 20 + FULLNAME = u'channel.flow' + SYNCHRONOUS = True + REPLY_WITH = [ChannelFlowOk] + FIELDS = [ + (u'active', u'bit', u'bit'), # start/stop content frames + ] + + def __init__(self, active): + """ + Create frame channel.flow + + :param active: Start/stop content frames + If 1, the peer starts sending content frames. If 0, the peer stops sending + content frames. + :type active: bit (as bit) + """ + self.active = active + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ChannelFlowOk(AMQPMethod): + """ + Confirm a flow method + + Confirms to the peer that a flow command was received and processed. + """ + CLASS = Channel + NAME = u'flow-ok' + CLASSNAME = u'channel' + CLASS_INDEX = 20 + METHOD_INDEX = 21 + FULLNAME = u'channel.flow-ok' + SYNCHRONOUS = False + REPLY_WITH = [] + RESPONSE_TO = ChannelFlow + FIELDS = [ + (u'active', u'bit', u'bit'), # current flow setting + ] + + def __init__(self, active): + """ + Create frame channel.flow-ok + + :param active: Current flow setting + Confirms the setting of the processed flow method: 1 means the peer will start + sending or continue to send content frames; 0 means it will not. + :type active: bit (as bit) + """ + self.active = active + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ChannelOpen(AMQPMethod): + """ + Open a channel for use + + This method opens a channel to the server. + """ + CLASS = Channel + NAME = u'open' + CLASSNAME = u'channel' + CLASS_INDEX = 20 + METHOD_INDEX = 10 + FULLNAME = u'channel.open' + SYNCHRONOUS = True + REPLY_WITH = [ChannelOpenOk] + FIELDS = [ + (u'reserved-1', u'shortstr', u'shortstr'), + ] + + def __init__(self): + """ + Create frame channel.open + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ChannelOpenOk(AMQPMethod): + """ + Signal that the channel is ready + + This method signals to the client that the channel is ready for use. + """ + CLASS = Channel + NAME = u'open-ok' + CLASSNAME = u'channel' + CLASS_INDEX = 20 + METHOD_INDEX = 11 + FULLNAME = u'channel.open-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = ChannelOpen + FIELDS = [ + (u'reserved-1', u'longstr', u'longstr'), + ] + + def __init__(self): + """ + Create frame channel.open-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + +class Exchange(AMQPClass): + """ + Exchanges match and distribute messages across queues. exchanges can be configured in + + the server or declared at runtime. + """ + NAME = u'exchange' + INDEX = 40 + + +class ExchangeDeclare(AMQPMethod): + """ + Verify exchange exists, create if needed + + This method creates an exchange if it does not already exist, and if the exchange + exists, verifies that it is of the correct and expected class. + """ + CLASS = Exchange + NAME = u'declare' + CLASSNAME = u'exchange' + CLASS_INDEX = 40 + METHOD_INDEX = 10 + FULLNAME = u'exchange.declare' + SYNCHRONOUS = True + REPLY_WITH = [ExchangeDeclareOk] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'exchange', u'exchange-name', u'shortstr'), + (u'type', u'shortstr', u'shortstr'), # exchange type + (u'passive', u'bit', u'bit'), # do not create exchange + (u'durable', u'bit', u'bit'), # request a durable exchange + (u'reserved-2', u'bit', u'bit'), + (u'reserved-3', u'bit', u'bit'), + (u'no-wait', u'no-wait', u'bit'), + (u'arguments', u'table', u'table'), # arguments for declaration + ] + + def __init__(self, exchange, type, passive, durable, no_wait, arguments): + """ + Create frame exchange.declare + + :param exchange: Exchange names starting with "amq." are reserved for pre-declared and + standardised exchanges. The client MAY declare an exchange starting with + "amq." if the passive option is set, or the exchange already exists. + :type exchange: exchange-name (as shortstr) + :param type: Exchange type + Each exchange belongs to one of a set of exchange types implemented by the + server. The exchange types define the functionality of the exchange - i.e. how + messages are routed through it. It is not valid or meaningful to attempt to + change the type of an existing exchange. + :type type: shortstr (as shortstr) + :param passive: Do not create exchange + If set, the server will reply with Declare-Ok if the exchange already + exists with the same name, and raise an error if not. The client can + use this to check whether an exchange exists without modifying the + server state. When set, all other method fields except name and no-wait + are ignored. A declare with both passive and no-wait has no effect. + Arguments are compared for semantic equivalence. + :type passive: bit (as bit) + :param durable: Request a durable exchange + If set when creating a new exchange, the exchange will be marked as durable. + Durable exchanges remain active when a server restarts. Non-durable exchanges + (transient exchanges) are purged if/when a server restarts. + :type durable: bit (as bit) + :type no_wait: no-wait (as bit) + :param arguments: Arguments for declaration + A set of arguments for the declaration. The syntax and semantics of these + arguments depends on the server implementation. + :type arguments: table (as table) + """ + self.exchange = exchange + self.type = type + self.passive = passive + self.durable = durable + self.no_wait = no_wait + self.arguments = arguments + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ExchangeDelete(AMQPMethod): + """ + Delete an exchange + + This method deletes an exchange. When an exchange is deleted all queue bindings on + the exchange are cancelled. + """ + CLASS = Exchange + NAME = u'delete' + CLASSNAME = u'exchange' + CLASS_INDEX = 40 + METHOD_INDEX = 20 + FULLNAME = u'exchange.delete' + SYNCHRONOUS = True + REPLY_WITH = [ExchangeDeleteOk] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'exchange', u'exchange-name', u'shortstr'), + (u'if-unused', u'bit', u'bit'), # delete only if unused + (u'no-wait', u'no-wait', u'bit'), + ] + + def __init__(self, exchange, if_unused, no_wait): + """ + Create frame exchange.delete + + :param exchange: The client must not attempt to delete an exchange that does not exist. + + :type exchange: exchange-name (as shortstr) + :param if_unused: Delete only if unused + If set, the server will only delete the exchange if it has no queue bindings. If + the exchange has queue bindings the server does not delete it but raises a + channel exception instead. + :type if_unused: bit (as bit) + :type no_wait: no-wait (as bit) + """ + self.exchange = exchange + self.if_unused = if_unused + self.no_wait = no_wait + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ExchangeDeclareOk(AMQPMethod): + """ + Confirm exchange declaration + + This method confirms a Declare method and confirms the name of the exchange, + essential for automatically-named exchanges. + """ + CLASS = Exchange + NAME = u'declare-ok' + CLASSNAME = u'exchange' + CLASS_INDEX = 40 + METHOD_INDEX = 11 + FULLNAME = u'exchange.declare-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = ExchangeDeclare + FIELDS = [ + ] + + def __init__(self): + """ + Create frame exchange.declare-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class ExchangeDeleteOk(AMQPMethod): + """ + Confirm deletion of an exchange + + This method confirms the deletion of an exchange. + """ + CLASS = Exchange + NAME = u'delete-ok' + CLASSNAME = u'exchange' + CLASS_INDEX = 40 + METHOD_INDEX = 21 + FULLNAME = u'exchange.delete-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = ExchangeDelete + FIELDS = [ + ] + + def __init__(self): + """ + Create frame exchange.delete-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + +class Queue(AMQPClass): + """ + Queues store and forward messages. queues can be configured in the server or created at + + runtime. Queues must be attached to at least one exchange in order to receive messages + from publishers. + """ + NAME = u'queue' + INDEX = 50 + + +class QueueBind(AMQPMethod): + """ + Bind queue to an exchange + + This method binds a queue to an exchange. Until a queue is bound it will not + receive any messages. In a classic messaging model, store-and-forward queues + are bound to a direct exchange and subscription queues are bound to a topic + exchange. + """ + CLASS = Queue + NAME = u'bind' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 20 + FULLNAME = u'queue.bind' + SYNCHRONOUS = True + REPLY_WITH = [QueueBindOk] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'queue', u'queue-name', u'shortstr'), + (u'exchange', u'exchange-name', u'shortstr'), # name of the exchange to bind to + (u'routing-key', u'shortstr', u'shortstr'), # message routing key + (u'no-wait', u'no-wait', u'bit'), + (u'arguments', u'table', u'table'), # arguments for binding + ] + + def __init__(self, queue, exchange, routing_key, no_wait, arguments): + """ + Create frame queue.bind + + :param queue: Specifies the name of the queue to bind. + + :type queue: queue-name (as shortstr) + :param exchange: Name of the exchange to bind to + A client MUST NOT be allowed to bind a queue to a non-existent exchange. + :type exchange: exchange-name (as shortstr) + :param routing_key: Message routing key + Specifies the routing key for the binding. The routing key is used for routing + messages depending on the exchange configuration. Not all exchanges use a + routing key - refer to the specific exchange documentation. If the queue name + is empty, the server uses the last queue declared on the channel. If the + routing key is also empty, the server uses this queue name for the routing + key as well. If the queue name is provided but the routing key is empty, the + server does the binding with that empty routing key. The meaning of empty + routing keys depends on the exchange implementation. + :type routing_key: shortstr (as shortstr) + :type no_wait: no-wait (as bit) + :param arguments: Arguments for binding + A set of arguments for the binding. The syntax and semantics of these arguments + depends on the exchange class. + :type arguments: table (as table) + """ + self.queue = queue + self.exchange = exchange + self.routing_key = routing_key + self.no_wait = no_wait + self.arguments = arguments + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class QueueBindOk(AMQPMethod): + """ + Confirm bind successful + + This method confirms that the bind was successful. + """ + CLASS = Queue + NAME = u'bind-ok' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 21 + FULLNAME = u'queue.bind-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = QueueBind + FIELDS = [ + ] + + def __init__(self): + """ + Create frame queue.bind-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class QueueDeclare(AMQPMethod): + """ + Declare queue, create if needed + + This method creates or checks a queue. When creating a new queue the client can + specify various properties that control the durability of the queue and its + contents, and the level of sharing for the queue. + """ + CLASS = Queue + NAME = u'declare' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 10 + FULLNAME = u'queue.declare' + SYNCHRONOUS = True + REPLY_WITH = [QueueDeclareOk] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'queue', u'queue-name', u'shortstr'), + (u'passive', u'bit', u'bit'), # do not create queue + (u'durable', u'bit', u'bit'), # request a durable queue + (u'exclusive', u'bit', u'bit'), # request an exclusive queue + (u'auto-delete', u'bit', u'bit'), # auto-delete queue when unused + (u'no-wait', u'no-wait', u'bit'), + (u'arguments', u'table', u'table'), # arguments for declaration + ] + + def __init__(self, queue, passive, durable, exclusive, auto_delete, no_wait, arguments): + """ + Create frame queue.declare + + :param queue: The queue name may be empty, in which case the server must create a new + queue with a unique generated name and return this to the client in the + Declare-Ok method. + :type queue: queue-name (as shortstr) + :param passive: Do not create queue + If set, the server will reply with Declare-Ok if the queue already + exists with the same name, and raise an error if not. The client can + use this to check whether a queue exists without modifying the + server state. When set, all other method fields except name and no-wait + are ignored. A declare with both passive and no-wait has no effect. + Arguments are compared for semantic equivalence. + :type passive: bit (as bit) + :param durable: Request a durable queue + If set when creating a new queue, the queue will be marked as durable. Durable + queues remain active when a server restarts. Non-durable queues (transient + queues) are purged if/when a server restarts. Note that durable queues do not + necessarily hold persistent messages, although it does not make sense to send + persistent messages to a transient queue. + :type durable: bit (as bit) + :param exclusive: Request an exclusive queue + Exclusive queues may only be accessed by the current connection, and are + deleted when that connection closes. Passive declaration of an exclusive + queue by other connections are not allowed. + :type exclusive: bit (as bit) + :param auto_delete: Auto-delete queue when unused + If set, the queue is deleted when all consumers have finished using it. The last + consumer can be cancelled either explicitly or because its channel is closed. If + there was no consumer ever on the queue, it won't be deleted. Applications can + explicitly delete auto-delete queues using the Delete method as normal. + :type auto_delete: bit (as bit) + :type no_wait: no-wait (as bit) + :param arguments: Arguments for declaration + A set of arguments for the declaration. The syntax and semantics of these + arguments depends on the server implementation. + :type arguments: table (as table) + """ + self.queue = queue + self.passive = passive + self.durable = durable + self.exclusive = exclusive + self.auto_delete = auto_delete + self.no_wait = no_wait + self.arguments = arguments + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class QueueDelete(AMQPMethod): + """ + Delete a queue + + This method deletes a queue. When a queue is deleted any pending messages are sent + to a dead-letter queue if this is defined in the server configuration, and all + consumers on the queue are cancelled. + """ + CLASS = Queue + NAME = u'delete' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 40 + FULLNAME = u'queue.delete' + SYNCHRONOUS = True + REPLY_WITH = [QueueDeleteOk] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'queue', u'queue-name', u'shortstr'), + (u'if-unused', u'bit', u'bit'), # delete only if unused + (u'if-empty', u'bit', u'bit'), # delete only if empty + (u'no-wait', u'no-wait', u'bit'), + ] + + def __init__(self, queue, if_unused, if_empty, no_wait): + """ + Create frame queue.delete + + :param queue: Specifies the name of the queue to delete. + + :type queue: queue-name (as shortstr) + :param if_unused: Delete only if unused + If set, the server will only delete the queue if it has no consumers. If the + queue has consumers the server does does not delete it but raises a channel + exception instead. + :type if_unused: bit (as bit) + :param if_empty: Delete only if empty + If set, the server will only delete the queue if it has no messages. + :type if_empty: bit (as bit) + :type no_wait: no-wait (as bit) + """ + self.queue = queue + self.if_unused = if_unused + self.if_empty = if_empty + self.no_wait = no_wait + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class QueueDeclareOk(AMQPMethod): + """ + Confirms a queue definition + + This method confirms a Declare method and confirms the name of the queue, essential + for automatically-named queues. + """ + CLASS = Queue + NAME = u'declare-ok' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 11 + FULLNAME = u'queue.declare-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = QueueDeclare + FIELDS = [ + (u'queue', u'queue-name', u'shortstr'), + (u'message-count', u'message-count', u'long'), + (u'consumer-count', u'long', u'long'), # number of consumers + ] + + def __init__(self, queue, message_count, consumer_count): + """ + Create frame queue.declare-ok + + :param queue: Reports the name of the queue. if the server generated a queue name, this field + contains that name. + :type queue: queue-name (as shortstr) + :type message_count: message-count (as long) + :param consumer_count: Number of consumers + Reports the number of active consumers for the queue. Note that consumers can + suspend activity (Channel.Flow) in which case they do not appear in this count. + :type consumer_count: long (as long) + """ + self.queue = queue + self.message_count = message_count + self.consumer_count = consumer_count + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class QueueDeleteOk(AMQPMethod): + """ + Confirm deletion of a queue + + This method confirms the deletion of a queue. + """ + CLASS = Queue + NAME = u'delete-ok' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 41 + FULLNAME = u'queue.delete-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = QueueDelete + FIELDS = [ + (u'message-count', u'message-count', u'long'), + ] + + def __init__(self, message_count): + """ + Create frame queue.delete-ok + + :param message_count: Reports the number of messages deleted. + + :type message_count: message-count (as long) + """ + self.message_count = message_count + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class QueuePurge(AMQPMethod): + """ + Purge a queue + + This method removes all messages from a queue which are not awaiting + acknowledgment. + """ + CLASS = Queue + NAME = u'purge' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 30 + FULLNAME = u'queue.purge' + SYNCHRONOUS = True + REPLY_WITH = [QueuePurgeOk] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'queue', u'queue-name', u'shortstr'), + (u'no-wait', u'no-wait', u'bit'), + ] + + def __init__(self, queue, no_wait): + """ + Create frame queue.purge + + :param queue: Specifies the name of the queue to purge. + + :type queue: queue-name (as shortstr) + :type no_wait: no-wait (as bit) + """ + self.queue = queue + self.no_wait = no_wait + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class QueuePurgeOk(AMQPMethod): + """ + Confirms a queue purge + + This method confirms the purge of a queue. + """ + CLASS = Queue + NAME = u'purge-ok' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 31 + FULLNAME = u'queue.purge-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = QueuePurge + FIELDS = [ + (u'message-count', u'message-count', u'long'), + ] + + def __init__(self, message_count): + """ + Create frame queue.purge-ok + + :param message_count: Reports the number of messages purged. + + :type message_count: message-count (as long) + """ + self.message_count = message_count + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class QueueUnbind(AMQPMethod): + """ + Unbind a queue from an exchange + + This method unbinds a queue from an exchange. + """ + CLASS = Queue + NAME = u'unbind' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 50 + FULLNAME = u'queue.unbind' + SYNCHRONOUS = True + REPLY_WITH = [QueueUnbindOk] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'queue', u'queue-name', u'shortstr'), + (u'exchange', u'exchange-name', u'shortstr'), + (u'routing-key', u'shortstr', u'shortstr'), # routing key of binding + (u'arguments', u'table', u'table'), # arguments of binding + ] + + def __init__(self, queue, exchange, routing_key, arguments): + """ + Create frame queue.unbind + + :param queue: Specifies the name of the queue to unbind. + + :type queue: queue-name (as shortstr) + :param exchange: The name of the exchange to unbind from. + + :type exchange: exchange-name (as shortstr) + :param routing_key: Routing key of binding + Specifies the routing key of the binding to unbind. + :type routing_key: shortstr (as shortstr) + :param arguments: Arguments of binding + Specifies the arguments of the binding to unbind. + :type arguments: table (as table) + """ + self.queue = queue + self.exchange = exchange + self.routing_key = routing_key + self.arguments = arguments + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class QueueUnbindOk(AMQPMethod): + """ + Confirm unbind successful + + This method confirms that the unbind was successful. + """ + CLASS = Queue + NAME = u'unbind-ok' + CLASSNAME = u'queue' + CLASS_INDEX = 50 + METHOD_INDEX = 51 + FULLNAME = u'queue.unbind-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = QueueUnbind + FIELDS = [ + ] + + def __init__(self): + """ + Create frame queue.unbind-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + +class Basic(AMQPClass): + """ + The basic class provides methods that support an industry-standard messaging model. + + """ + NAME = u'basic' + INDEX = 60 + + +class BasicAck(AMQPMethod): + """ + Acknowledge one or more messages + + This method acknowledges one or more messages delivered via the Deliver or Get-Ok + methods. The client can ask to confirm a single message or a set of messages up to + and including a specific message. + """ + CLASS = Basic + NAME = u'ack' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 80 + FULLNAME = u'basic.ack' + SYNCHRONOUS = False + REPLY_WITH = [] + FIELDS = [ + (u'delivery-tag', u'delivery-tag', u'longlong'), + (u'multiple', u'bit', u'bit'), # acknowledge multiple messages + ] + + def __init__(self, delivery_tag, multiple): + """ + Create frame basic.ack + + :type delivery_tag: delivery-tag (as longlong) + :param multiple: Acknowledge multiple messages + If set to 1, the delivery tag is treated as "up to and including", so that the + client can acknowledge multiple messages with a single method. If set to zero, + the delivery tag refers to a single message. If the multiple field is 1, and the + delivery tag is zero, tells the server to acknowledge all outstanding messages. + :type multiple: bit (as bit) + """ + self.delivery_tag = delivery_tag + self.multiple = multiple + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicConsume(AMQPMethod): + """ + Start a queue consumer + + This method asks the server to start a "consumer", which is a transient request for + messages from a specific queue. Consumers last as long as the channel they were + declared on, or until the client cancels them. + """ + CLASS = Basic + NAME = u'consume' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 20 + FULLNAME = u'basic.consume' + SYNCHRONOUS = True + REPLY_WITH = [BasicConsumeOk] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'queue', u'queue-name', u'shortstr'), + (u'consumer-tag', u'consumer-tag', u'shortstr'), + (u'no-local', u'no-local', u'bit'), + (u'no-ack', u'no-ack', u'bit'), + (u'exclusive', u'bit', u'bit'), # request exclusive access + (u'no-wait', u'no-wait', u'bit'), + (u'arguments', u'table', u'table'), # arguments for declaration + ] + + def __init__(self, queue, consumer_tag, no_local, no_ack, exclusive, no_wait, arguments): + """ + Create frame basic.consume + + :param queue: Specifies the name of the queue to consume from. + + :type queue: queue-name (as shortstr) + :param consumer_tag: Specifies the identifier for the consumer. the consumer tag is local to a + channel, so two clients can use the same consumer tags. If this field is + empty the server will generate a unique tag. + :type consumer_tag: consumer-tag (as shortstr) + :type no_local: no-local (as bit) + :type no_ack: no-ack (as bit) + :param exclusive: Request exclusive access + Request exclusive consumer access, meaning only this consumer can access the + queue. + :type exclusive: bit (as bit) + :type no_wait: no-wait (as bit) + :param arguments: Arguments for declaration + A set of arguments for the consume. The syntax and semantics of these + arguments depends on the server implementation. + :type arguments: table (as table) + """ + self.queue = queue + self.consumer_tag = consumer_tag + self.no_local = no_local + self.no_ack = no_ack + self.exclusive = exclusive + self.no_wait = no_wait + self.arguments = arguments + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicCancel(AMQPMethod): + """ + End a queue consumer + + This method cancels a consumer. This does not affect already delivered + messages, but it does mean the server will not send any more messages for + that consumer. The client may receive an arbitrary number of messages in + between sending the cancel method and receiving the cancel-ok reply. + """ + CLASS = Basic + NAME = u'cancel' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 30 + FULLNAME = u'basic.cancel' + SYNCHRONOUS = True + REPLY_WITH = [BasicCancelOk] + FIELDS = [ + (u'consumer-tag', u'consumer-tag', u'shortstr'), + (u'no-wait', u'no-wait', u'bit'), + ] + + def __init__(self, consumer_tag, no_wait): + """ + Create frame basic.cancel + + :type consumer_tag: consumer-tag (as shortstr) + :type no_wait: no-wait (as bit) + """ + self.consumer_tag = consumer_tag + self.no_wait = no_wait + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicConsumeOk(AMQPMethod): + """ + Confirm a new consumer + + The server provides the client with a consumer tag, which is used by the client + for methods called on the consumer at a later stage. + """ + CLASS = Basic + NAME = u'consume-ok' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 21 + FULLNAME = u'basic.consume-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = BasicConsume + FIELDS = [ + (u'consumer-tag', u'consumer-tag', u'shortstr'), + ] + + def __init__(self, consumer_tag): + """ + Create frame basic.consume-ok + + :param consumer_tag: Holds the consumer tag specified by the client or provided by the server. + + :type consumer_tag: consumer-tag (as shortstr) + """ + self.consumer_tag = consumer_tag + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicCancelOk(AMQPMethod): + """ + Confirm a cancelled consumer + + This method confirms that the cancellation was completed. + """ + CLASS = Basic + NAME = u'cancel-ok' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 31 + FULLNAME = u'basic.cancel-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = BasicCancel + FIELDS = [ + (u'consumer-tag', u'consumer-tag', u'shortstr'), + ] + + def __init__(self, consumer_tag): + """ + Create frame basic.cancel-ok + + :type consumer_tag: consumer-tag (as shortstr) + """ + self.consumer_tag = consumer_tag + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicDeliver(AMQPMethod): + """ + Notify the client of a consumer message + + This method delivers a message to the client, via a consumer. In the asynchronous + message delivery model, the client starts a consumer using the Consume method, then + the server responds with Deliver methods as and when messages arrive for that + consumer. + """ + CLASS = Basic + NAME = u'deliver' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 60 + FULLNAME = u'basic.deliver' + SYNCHRONOUS = False + REPLY_WITH = [] + FIELDS = [ + (u'consumer-tag', u'consumer-tag', u'shortstr'), + (u'delivery-tag', u'delivery-tag', u'longlong'), + (u'redelivered', u'redelivered', u'bit'), + (u'exchange', u'exchange-name', u'shortstr'), + (u'routing-key', u'shortstr', u'shortstr'), # Message routing key + ] + + def __init__(self, consumer_tag, delivery_tag, redelivered, exchange, routing_key): + """ + Create frame basic.deliver + + :type consumer_tag: consumer-tag (as shortstr) + :type delivery_tag: delivery-tag (as longlong) + :type redelivered: redelivered (as bit) + :param exchange: Specifies the name of the exchange that the message was originally published to. + May be empty, indicating the default exchange. + :type exchange: exchange-name (as shortstr) + :param routing_key: Message routing key + Specifies the routing key name specified when the message was published. + :type routing_key: shortstr (as shortstr) + """ + self.consumer_tag = consumer_tag + self.delivery_tag = delivery_tag + self.redelivered = redelivered + self.exchange = exchange + self.routing_key = routing_key + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicGet(AMQPMethod): + """ + Direct access to a queue + + This method provides a direct access to the messages in a queue using a synchronous + dialogue that is designed for specific types of application where synchronous + functionality is more important than performance. + """ + CLASS = Basic + NAME = u'get' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 70 + FULLNAME = u'basic.get' + SYNCHRONOUS = True + REPLY_WITH = [BasicGetOk, BasicGetEmpty] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'queue', u'queue-name', u'shortstr'), + (u'no-ack', u'no-ack', u'bit'), + ] + + def __init__(self, queue, no_ack): + """ + Create frame basic.get + + :param queue: Specifies the name of the queue to get a message from. + + :type queue: queue-name (as shortstr) + :type no_ack: no-ack (as bit) + """ + self.queue = queue + self.no_ack = no_ack + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicGetOk(AMQPMethod): + """ + Provide client with a message + + This method delivers a message to the client following a get method. A message + delivered by 'get-ok' must be acknowledged unless the no-ack option was set in the + get method. + """ + CLASS = Basic + NAME = u'get-ok' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 71 + FULLNAME = u'basic.get-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = BasicGet + FIELDS = [ + (u'delivery-tag', u'delivery-tag', u'longlong'), + (u'redelivered', u'redelivered', u'bit'), + (u'exchange', u'exchange-name', u'shortstr'), + (u'routing-key', u'shortstr', u'shortstr'), # Message routing key + (u'message-count', u'message-count', u'long'), + ] + + def __init__(self, delivery_tag, redelivered, exchange, routing_key, message_count): + """ + Create frame basic.get-ok + + :type delivery_tag: delivery-tag (as longlong) + :type redelivered: redelivered (as bit) + :param exchange: Specifies the name of the exchange that the message was originally published to. + If empty, the message was published to the default exchange. + :type exchange: exchange-name (as shortstr) + :param routing_key: Message routing key + Specifies the routing key name specified when the message was published. + :type routing_key: shortstr (as shortstr) + :type message_count: message-count (as long) + """ + self.delivery_tag = delivery_tag + self.redelivered = redelivered + self.exchange = exchange + self.routing_key = routing_key + self.message_count = message_count + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicGetEmpty(AMQPMethod): + """ + Indicate no messages available + + This method tells the client that the queue has no messages available for the + client. + """ + CLASS = Basic + NAME = u'get-empty' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 72 + FULLNAME = u'basic.get-empty' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = BasicGet + FIELDS = [ + (u'reserved-1', u'shortstr', u'shortstr'), + ] + + def __init__(self): + """ + Create frame basic.get-empty + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicPublish(AMQPMethod): + """ + Publish a message + + This method publishes a message to a specific exchange. The message will be routed + to queues as defined by the exchange configuration and distributed to any active + consumers when the transaction, if any, is committed. + """ + CLASS = Basic + NAME = u'publish' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 40 + FULLNAME = u'basic.publish' + SYNCHRONOUS = False + REPLY_WITH = [] + FIELDS = [ + (u'reserved-1', u'short', u'short'), + (u'exchange', u'exchange-name', u'shortstr'), + (u'routing-key', u'shortstr', u'shortstr'), # Message routing key + (u'mandatory', u'bit', u'bit'), # indicate mandatory routing + (u'immediate', u'bit', u'bit'), # request immediate delivery + ] + + def __init__(self, exchange, routing_key, mandatory, immediate): + """ + Create frame basic.publish + + :param exchange: Specifies the name of the exchange to publish to. the exchange name can be + empty, meaning the default exchange. If the exchange name is specified, and that + exchange does not exist, the server will raise a channel exception. + :type exchange: exchange-name (as shortstr) + :param routing_key: Message routing key + Specifies the routing key for the message. The routing key is used for routing + messages depending on the exchange configuration. + :type routing_key: shortstr (as shortstr) + :param mandatory: Indicate mandatory routing + This flag tells the server how to react if the message cannot be routed to a + queue. If this flag is set, the server will return an unroutable message with a + Return method. If this flag is zero, the server silently drops the message. + :type mandatory: bit (as bit) + :param immediate: Request immediate delivery + This flag tells the server how to react if the message cannot be routed to a + queue consumer immediately. If this flag is set, the server will return an + undeliverable message with a Return method. If this flag is zero, the server + will queue the message, but with no guarantee that it will ever be consumed. + :type immediate: bit (as bit) + """ + self.exchange = exchange + self.routing_key = routing_key + self.mandatory = mandatory + self.immediate = immediate + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicQos(AMQPMethod): + """ + Specify quality of service + + This method requests a specific quality of service. The QoS can be specified for the + current channel or for all channels on the connection. The particular properties and + semantics of a qos method always depend on the content class semantics. Though the + qos method could in principle apply to both peers, it is currently meaningful only + for the server. + """ + CLASS = Basic + NAME = u'qos' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 10 + FULLNAME = u'basic.qos' + SYNCHRONOUS = True + REPLY_WITH = [BasicQosOk] + FIELDS = [ + (u'prefetch-size', u'long', u'long'), # prefetch window in octets + (u'prefetch-count', u'short', u'short'), # prefetch window in messages + (u'global', u'bit', u'bit'), # apply to entire connection + ] + + def __init__(self, prefetch_size, prefetch_count, global_): + """ + Create frame basic.qos + + :param prefetch_size: Prefetch window in octets + The client can request that messages be sent in advance so that when the client + finishes processing a message, the following message is already held locally, + rather than needing to be sent down the channel. Prefetching gives a performance + improvement. This field specifies the prefetch window size in octets. The server + will send a message in advance if it is equal to or smaller in size than the + available prefetch size (and also falls into other prefetch limits). May be set + to zero, meaning "no specific limit", although other prefetch limits may still + apply. The prefetch-size is ignored if the no-ack option is set. + :type prefetch_size: long (as long) + :param prefetch_count: Prefetch window in messages + Specifies a prefetch window in terms of whole messages. This field may be used + in combination with the prefetch-size field; a message will only be sent in + advance if both prefetch windows (and those at the channel and connection level) + allow it. The prefetch-count is ignored if the no-ack option is set. + :type prefetch_count: short (as short) + :param global_: Apply to entire connection + By default the QoS settings apply to the current channel only. If this field is + set, they are applied to the entire connection. + :type global_: bit (as bit) + """ + self.prefetch_size = prefetch_size + self.prefetch_count = prefetch_count + self.global_ = global_ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicQosOk(AMQPMethod): + """ + Confirm the requested qos + + This method tells the client that the requested QoS levels could be handled by the + server. The requested QoS applies to all active consumers until a new QoS is + defined. + """ + CLASS = Basic + NAME = u'qos-ok' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 11 + FULLNAME = u'basic.qos-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = BasicQos + FIELDS = [ + ] + + def __init__(self): + """ + Create frame basic.qos-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicReturn(AMQPMethod): + """ + Return a failed message + + This method returns an undeliverable message that was published with the "immediate" + flag set, or an unroutable message published with the "mandatory" flag set. The + reply code and text provide information about the reason that the message was + undeliverable. + """ + CLASS = Basic + NAME = u'return' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 50 + FULLNAME = u'basic.return' + SYNCHRONOUS = False + REPLY_WITH = [] + FIELDS = [ + (u'reply-code', u'reply-code', u'short'), + (u'reply-text', u'reply-text', u'shortstr'), + (u'exchange', u'exchange-name', u'shortstr'), + (u'routing-key', u'shortstr', u'shortstr'), # Message routing key + ] + + def __init__(self, reply_code, reply_text, exchange, routing_key): + """ + Create frame basic.return + + :type reply_code: reply-code (as short) + :type reply_text: reply-text (as shortstr) + :param exchange: Specifies the name of the exchange that the message was originally published + to. May be empty, meaning the default exchange. + :type exchange: exchange-name (as shortstr) + :param routing_key: Message routing key + Specifies the routing key name specified when the message was published. + :type routing_key: shortstr (as shortstr) + """ + self.reply_code = reply_code + self.reply_text = reply_text + self.exchange = exchange + self.routing_key = routing_key + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicReject(AMQPMethod): + """ + Reject an incoming message + + This method allows a client to reject a message. It can be used to interrupt and + cancel large incoming messages, or return untreatable messages to their original + queue. + """ + CLASS = Basic + NAME = u'reject' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 90 + FULLNAME = u'basic.reject' + SYNCHRONOUS = False + REPLY_WITH = [] + FIELDS = [ + (u'delivery-tag', u'delivery-tag', u'longlong'), + (u'requeue', u'bit', u'bit'), # requeue the message + ] + + def __init__(self, delivery_tag, requeue): + """ + Create frame basic.reject + + :type delivery_tag: delivery-tag (as longlong) + :param requeue: Requeue the message + If requeue is true, the server will attempt to requeue the message. If requeue + is false or the requeue attempt fails the messages are discarded or dead-lettered. + :type requeue: bit (as bit) + """ + self.delivery_tag = delivery_tag + self.requeue = requeue + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicRecoverAsync(AMQPMethod): + """ + Redeliver unacknowledged messages + + This method asks the server to redeliver all unacknowledged messages on a + specified channel. Zero or more messages may be redelivered. This method + is deprecated in favour of the synchronous Recover/Recover-Ok. + """ + CLASS = Basic + NAME = u'recover-async' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 100 + FULLNAME = u'basic.recover-async' + SYNCHRONOUS = False + REPLY_WITH = [] + FIELDS = [ + (u'requeue', u'bit', u'bit'), # requeue the message + ] + + def __init__(self, requeue): + """ + Create frame basic.recover-async + + :param requeue: Requeue the message + If this field is zero, the message will be redelivered to the original + recipient. If this bit is 1, the server will attempt to requeue the message, + potentially then delivering it to an alternative subscriber. + :type requeue: bit (as bit) + """ + self.requeue = requeue + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicRecover(AMQPMethod): + """ + Redeliver unacknowledged messages + + This method asks the server to redeliver all unacknowledged messages on a + specified channel. Zero or more messages may be redelivered. This method + replaces the asynchronous Recover. + """ + CLASS = Basic + NAME = u'recover' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 110 + FULLNAME = u'basic.recover' + SYNCHRONOUS = False + REPLY_WITH = [] + FIELDS = [ + (u'requeue', u'bit', u'bit'), # requeue the message + ] + + def __init__(self, requeue): + """ + Create frame basic.recover + + :param requeue: Requeue the message + If this field is zero, the message will be redelivered to the original + recipient. If this bit is 1, the server will attempt to requeue the message, + potentially then delivering it to an alternative subscriber. + :type requeue: bit (as bit) + """ + self.requeue = requeue + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class BasicRecoverOk(AMQPMethod): + """ + Confirm recovery + + This method acknowledges a Basic.Recover method. + """ + CLASS = Basic + NAME = u'recover-ok' + CLASSNAME = u'basic' + CLASS_INDEX = 60 + METHOD_INDEX = 111 + FULLNAME = u'basic.recover-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + FIELDS = [ + ] + + def __init__(self): + """ + Create frame basic.recover-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + +class Tx(AMQPClass): + """ + The tx class allows publish and ack operations to be batched into atomic + + units of work. The intention is that all publish and ack requests issued + within a transaction will complete successfully or none of them will. + Servers SHOULD implement atomic transactions at least where all publish + or ack requests affect a single queue. Transactions that cover multiple + queues may be non-atomic, given that queues can be created and destroyed + asynchronously, and such events do not form part of any transaction. + Further, the behaviour of transactions with respect to the immediate and + mandatory flags on Basic.Publish methods is not defined. + """ + NAME = u'tx' + INDEX = 90 + + +class TxCommit(AMQPMethod): + """ + Commit the current transaction + + This method commits all message publications and acknowledgments performed in + the current transaction. A new transaction starts immediately after a commit. + """ + CLASS = Tx + NAME = u'commit' + CLASSNAME = u'tx' + CLASS_INDEX = 90 + METHOD_INDEX = 20 + FULLNAME = u'tx.commit' + SYNCHRONOUS = True + REPLY_WITH = [TxCommitOk] + FIELDS = [ + ] + + def __init__(self): + """ + Create frame tx.commit + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class TxCommitOk(AMQPMethod): + """ + Confirm a successful commit + + This method confirms to the client that the commit succeeded. Note that if a commit + fails, the server raises a channel exception. + """ + CLASS = Tx + NAME = u'commit-ok' + CLASSNAME = u'tx' + CLASS_INDEX = 90 + METHOD_INDEX = 21 + FULLNAME = u'tx.commit-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = TxCommit + FIELDS = [ + ] + + def __init__(self): + """ + Create frame tx.commit-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class TxRollback(AMQPMethod): + """ + Abandon the current transaction + + This method abandons all message publications and acknowledgments performed in + the current transaction. A new transaction starts immediately after a rollback. + Note that unacked messages will not be automatically redelivered by rollback; + if that is required an explicit recover call should be issued. + """ + CLASS = Tx + NAME = u'rollback' + CLASSNAME = u'tx' + CLASS_INDEX = 90 + METHOD_INDEX = 30 + FULLNAME = u'tx.rollback' + SYNCHRONOUS = True + REPLY_WITH = [TxRollbackOk] + FIELDS = [ + ] + + def __init__(self): + """ + Create frame tx.rollback + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class TxRollbackOk(AMQPMethod): + """ + Confirm successful rollback + + This method confirms to the client that the rollback succeeded. Note that if an + rollback fails, the server raises a channel exception. + """ + CLASS = Tx + NAME = u'rollback-ok' + CLASSNAME = u'tx' + CLASS_INDEX = 90 + METHOD_INDEX = 31 + FULLNAME = u'tx.rollback-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = TxRollback + FIELDS = [ + ] + + def __init__(self): + """ + Create frame tx.rollback-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class TxSelect(AMQPMethod): + """ + Select standard transaction mode + + This method sets the channel to use standard transactions. The client must use this + method at least once on a channel before using the Commit or Rollback methods. + """ + CLASS = Tx + NAME = u'select' + CLASSNAME = u'tx' + CLASS_INDEX = 90 + METHOD_INDEX = 10 + FULLNAME = u'tx.select' + SYNCHRONOUS = True + REPLY_WITH = [TxSelectOk] + FIELDS = [ + ] + + def __init__(self): + """ + Create frame tx.select + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +class TxSelectOk(AMQPMethod): + """ + Confirm transaction mode + + This method confirms to the client that the channel was successfully set to use + standard transactions. + """ + CLASS = Tx + NAME = u'select-ok' + CLASSNAME = u'tx' + CLASS_INDEX = 90 + METHOD_INDEX = 11 + FULLNAME = u'tx.select-ok' + SYNCHRONOUS = True + REPLY_WITH = [] + RESPONSE_TO = TxSelect + FIELDS = [ + ] + + def __init__(self): + """ + Create frame tx.select-ok + + """ + + def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + + +IDENT_TO_METHOD = { + (90, 21): TxCommitOk, + (60, 100): BasicRecoverAsync, + (10, 11): ConnectionStartOk, + (60, 40): BasicPublish, + (60, 50): BasicReturn, + (10, 51): ConnectionCloseOk, + (20, 20): ChannelFlow, + (60, 21): BasicConsumeOk, + (10, 21): ConnectionSecureOk, + (90, 30): TxRollback, + (90, 10): TxSelect, + (50, 11): QueueDeclareOk, + (60, 70): BasicGet, + (90, 11): TxSelectOk, + (10, 30): ConnectionTune, + (60, 11): BasicQosOk, + (60, 80): BasicAck, + (20, 21): ChannelFlowOk, + (60, 60): BasicDeliver, + (90, 31): TxRollbackOk, + (20, 40): ChannelClose, + (60, 71): BasicGetOk, + (50, 30): QueuePurge, + (10, 31): ConnectionTuneOk, + (10, 40): ConnectionOpen, + (60, 30): BasicCancel, + (50, 50): QueueUnbind, + (40, 10): ExchangeDeclare, + (10, 50): ConnectionClose, + (20, 10): ChannelOpen, + (20, 41): ChannelCloseOk, + (60, 110): BasicRecover, + (60, 90): BasicReject, + (50, 31): QueuePurgeOk, + (50, 40): QueueDelete, + (40, 20): ExchangeDelete, + (50, 20): QueueBind, + (10, 41): ConnectionOpenOk, + (60, 31): BasicCancelOk, + (90, 20): TxCommit, + (10, 10): ConnectionStart, + (60, 10): BasicQos, + (40, 11): ExchangeDeclareOk, + (40, 21): ExchangeDeleteOk, + (20, 11): ChannelOpenOk, + (60, 72): BasicGetEmpty, + (60, 111): BasicRecoverOk, + (60, 20): BasicConsume, + (10, 20): ConnectionSecure, + (50, 41): QueueDeleteOk, + (50, 51): QueueUnbindOk, + (50, 21): QueueBindOk, + (50, 10): QueueDeclare, +} + diff --git a/coolamqp/framing/serialization.py b/coolamqp/framing/serialization.py new file mode 100644 index 0000000000000000000000000000000000000000..1aacd3177cfc98a5e36aa5f6ec7332d9b6550c71 --- /dev/null +++ b/coolamqp/framing/serialization.py @@ -0,0 +1,19 @@ +# coding=UTF-8 +from __future__ import absolute_import, division, print_function +import struct +"""bytes <-> Frame's""" + +from coolamqp.framing.definitions import FRAME_END + +HDR = b'AMQP\x01\x01\x00\x09' + + + +def to_packet(type_, channel, payload, size=None, frame_end=None): + """ + :type payload: six.binary_type + :type frame_end: six.binary_type + """ + size = size or len(payload) + frame_end = frame_end or FRAME_END + return struct.pack('!BHI', type_, channel, size) + payload + FRAME_END diff --git a/resources/amqp0-9-1.xml b/resources/amqp0-9-1.xml new file mode 100644 index 0000000000000000000000000000000000000000..da785eb3bed370d7c11852fce8ef7fe7f366f2f6 --- /dev/null +++ b/resources/amqp0-9-1.xml @@ -0,0 +1,2843 @@ +<?xml version = "1.0"?> +<!-- + Copyright Notice + ================ + Copyright (c) 2006-2008 Cisco Systems, Credit Suisse, Deutsche Boerse + Systems, Envoy Technologies, Inc., Goldman Sachs, IONA Technologies PLC, + iMatix Corporation, JPMorgan Chase Bank Inc. N.A, Novell, Rabbit + Technologies Ltd., Red Hat, Inc., TWIST Process Innovations Ltd, WS02 + Inc. and 29West Inc. All rights reserved. + + License + ======= + Cisco Systems, Credit Suisse, Deutsche Boerse Systems, Envoy Technologies, + Inc., Goldman Sachs, IONA Technologies PLC, iMatix Corporation, JPMorgan + Chase Bank Inc. N.A, Novell, Rabbit Technologies Ltd., Red Hat, Inc., + TWIST Process Innovations Ltd, WS02, Inc. and 29West Inc. (collectively, + the "Authors") each hereby grants to you a worldwide, perpetual, + royalty-free, nontransferable, nonexclusive license to (i) copy, display, + distribute and implement the Advanced Messaging Queue Protocol ("AMQP") + Specification and (ii) the Licensed Claims that are held by the Authors, + all for the purpose of implementing the Advanced Messaging Queue Protocol + Specification. Your license and any rights under this Agreement will + terminate immediately without notice from any Author if you bring any + claim, suit, demand, or action related to the Advanced Messaging Queue + Protocol Specification against any Author. Upon termination, you shall + destroy all copies of the Advanced Messaging Queue Protocol Specification + in your possession or control. + + As used hereunder, "Licensed Claims" means those claims of a patent or + patent application, throughout the world, excluding design patents and + design registrations, owned or controlled, or that can be sublicensed + without fee and in compliance with the requirements of this Agreement, + by an Author or its affiliates now or at any future time and which would + necessarily be infringed by implementation of the Advanced Messaging + Queue Protocol Specification. A claim is necessarily infringed hereunder + only when it is not possible to avoid infringing it because there is no + plausible non-infringing alternative for implementing the required + portions of the Advanced Messaging Queue Protocol Specification. + Notwithstanding the foregoing, Licensed Claims shall not include any + claims other than as set forth above even if contained in the same patent + as Licensed Claims; or that read solely on any implementations of any + portion of the Advanced Messaging Queue Protocol Specification that are + not required by the Advanced Messaging Queue ProtocolSpecification, or + that, if licensed, would require a payment of royalties by the licensor + to unaffiliated third parties. Moreover, Licensed Claims shall not + include (i) any enabling technologies that may be necessary to make or + use any Licensed Product but are not themselves expressly set forth in + the Advanced Messaging Queue Protocol Specification (e.g., semiconductor + manufacturing technology, compiler technology, object oriented + technology, networking technology, operating system technology, and the + like); or (ii) the implementation of other published standards developed + elsewhere and merely referred to in the body of the Advanced Messaging + Queue Protocol Specification, or (iii) any Licensed Product and any + combinations thereof the purpose or function of which is not required + for compliance with the Advanced Messaging Queue Protocol Specification. + For purposes of this definition, the Advanced Messaging Queue Protocol + Specification shall be deemed to include both architectural and + interconnection requirements essential for interoperability and may also + include supporting source code artifacts where such architectural, + interconnection requirements and source code artifacts are expressly + identified as being required or documentation to achieve compliance with + the Advanced Messaging Queue Protocol Specification. + + As used hereunder, "Licensed Products" means only those specific portions + of products (hardware, software or combinations thereof) that implement + and are compliant with all relevant portions of the Advanced Messaging + Queue Protocol Specification. + + The following disclaimers, which you hereby also acknowledge as to any + use you may make of the Advanced Messaging Queue Protocol Specification: + + THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION IS PROVIDED "AS IS," + AND THE AUTHORS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE + CONTENTS OF THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION ARE + SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF THE ADVANCED + MESSAGING QUEUE PROTOCOL SPECIFICATION WILL NOT INFRINGE ANY THIRD PARTY + PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + + THE AUTHORS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, + INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO ANY + USE, IMPLEMENTATION OR DISTRIBUTION OF THE ADVANCED MESSAGING QUEUE + PROTOCOL SPECIFICATION. + + The name and trademarks of the Authors may NOT be used in any manner, + including advertising or publicity pertaining to the Advanced Messaging + Queue Protocol Specification or its contents without specific, written + prior permission. Title to copyright in the Advanced Messaging Queue + Protocol Specification will at all times remain with the Authors. + + No other rights are granted by implication, estoppel or otherwise. + + Upon termination of your license or rights under this Agreement, you + shall destroy all copies of the Advanced Messaging Queue Protocol + Specification in your possession or control. + + Trademarks + ========== + JPMorgan, JPMorgan Chase, Chase, the JPMorgan Chase logo and the + Octagon Symbol are trademarks of JPMorgan Chase & Co. + + IMATIX and the iMatix logo are trademarks of iMatix Corporation sprl. + + IONA, IONA Technologies, and the IONA logos are trademarks of IONA + Technologies PLC and/or its subsidiaries. + + LINUX is a trademark of Linus Torvalds. RED HAT and JBOSS are registered + trademarks of Red Hat, Inc. in the US and other countries. + + Java, all Java-based trademarks and OpenOffice.org are trademarks of + Sun Microsystems, Inc. in the United States, other countries, or both. + + Other company, product, or service names may be trademarks or service + marks of others. + + Links to full AMQP specification: + ================================= + http://www.amqp.org +--> + +<!-- + <!DOCTYPE amqp SYSTEM "amqp.dtd"> +--> + +<!-- XML Notes + + We use entities to indicate repetition; attributes to indicate properties. + + We use the 'name' attribute as an identifier, usually within the context + of the surrounding entities. + + We use spaces to seperate words in names, so that we can print names in + their natural form depending on the context - underlines for source code, + hyphens for written text, etc. + + We do not enforce any particular validation mechanism but we support all + mechanisms. The protocol definition conforms to a formal grammar that is + published seperately in several technologies. + + --> + +<amqp major = "0" minor = "9" revision = "1" + port = "5672" comment = "AMQ Protocol version 0-9-1"> + <!-- + ====================================================== + == CONSTANTS + ====================================================== + --> + <!-- Frame types --> + <constant name = "frame-method" value = "1" /> + <constant name = "frame-header" value = "2" /> + <constant name = "frame-body" value = "3" /> + <constant name = "frame-heartbeat" value = "8" /> + + <!-- Protocol constants --> + <constant name = "frame-min-size" value = "4096" /> + <constant name = "frame-end" value = "206" /> + + <!-- Reply codes --> + <constant name = "reply-success" value = "200"> + <doc> + Indicates that the method completed successfully. This reply code is + reserved for future use - the current protocol design does not use positive + confirmation and reply codes are sent only in case of an error. + </doc> + </constant> + + <constant name = "content-too-large" value = "311" class = "soft-error"> + <doc> + The client attempted to transfer content larger than the server could accept + at the present time. The client may retry at a later time. + </doc> + </constant> + + <constant name = "no-consumers" value = "313" class = "soft-error"> + <doc> + When the exchange cannot deliver to a consumer when the immediate flag is + set. As a result of pending data on the queue or the absence of any + consumers of the queue. + </doc> + </constant> + + <constant name = "connection-forced" value = "320" class = "hard-error"> + <doc> + An operator intervened to close the connection for some reason. The client + may retry at some later date. + </doc> + </constant> + + <constant name = "invalid-path" value = "402" class = "hard-error"> + <doc> + The client tried to work with an unknown virtual host. + </doc> + </constant> + + <constant name = "access-refused" value = "403" class = "soft-error"> + <doc> + The client attempted to work with a server entity to which it has no + access due to security settings. + </doc> + </constant> + + <constant name = "not-found" value = "404" class = "soft-error"> + <doc> + The client attempted to work with a server entity that does not exist. + </doc> + </constant> + + <constant name = "resource-locked" value = "405" class = "soft-error"> + <doc> + The client attempted to work with a server entity to which it has no + access because another client is working with it. + </doc> + </constant> + + <constant name = "precondition-failed" value = "406" class = "soft-error"> + <doc> + The client requested a method that was not allowed because some precondition + failed. + </doc> + </constant> + + <constant name = "frame-error" value = "501" class = "hard-error"> + <doc> + The sender sent a malformed frame that the recipient could not decode. + This strongly implies a programming error in the sending peer. + </doc> + </constant> + + <constant name = "syntax-error" value = "502" class = "hard-error"> + <doc> + The sender sent a frame that contained illegal values for one or more + fields. This strongly implies a programming error in the sending peer. + </doc> + </constant> + + <constant name = "command-invalid" value = "503" class = "hard-error"> + <doc> + The client sent an invalid sequence of frames, attempting to perform an + operation that was considered invalid by the server. This usually implies + a programming error in the client. + </doc> + </constant> + + <constant name = "channel-error" value = "504" class = "hard-error"> + <doc> + The client attempted to work with a channel that had not been correctly + opened. This most likely indicates a fault in the client layer. + </doc> + </constant> + + <constant name = "unexpected-frame" value = "505" class = "hard-error"> + <doc> + The peer sent a frame that was not expected, usually in the context of + a content header and body. This strongly indicates a fault in the peer's + content processing. + </doc> + </constant> + + <constant name = "resource-error" value = "506" class = "hard-error"> + <doc> + The server could not complete the method because it lacked sufficient + resources. This may be due to the client creating too many of some type + of entity. + </doc> + </constant> + + <constant name = "not-allowed" value = "530" class = "hard-error"> + <doc> + The client tried to work with some entity in a manner that is prohibited + by the server, due to security settings or by some other criteria. + </doc> + </constant> + + <constant name = "not-implemented" value = "540" class = "hard-error"> + <doc> + The client tried to use functionality that is not implemented in the + server. + </doc> + </constant> + + <constant name = "internal-error" value = "541" class = "hard-error"> + <doc> + The server could not complete the method because of an internal error. + The server may require intervention by an operator in order to resume + normal operations. + </doc> + </constant> + + <!-- + ====================================================== + == DOMAIN TYPES + ====================================================== + --> + + <domain name = "class-id" type = "short" /> + + <domain name = "consumer-tag" type = "shortstr" label = "consumer tag"> + <doc> + Identifier for the consumer, valid within the current channel. + </doc> + </domain> + + <domain name = "delivery-tag" type = "longlong" label = "server-assigned delivery tag"> + <doc> + The server-assigned and channel-specific delivery tag + </doc> + <rule name = "channel-local"> + <doc> + The delivery tag is valid only within the channel from which the message was + received. I.e. a client MUST NOT receive a message on one channel and then + acknowledge it on another. + </doc> + </rule> + <rule name = "non-zero"> + <doc> + The server MUST NOT use a zero value for delivery tags. Zero is reserved + for client use, meaning "all messages so far received". + </doc> + </rule> + </domain> + + <domain name = "exchange-name" type = "shortstr" label = "exchange name"> + <doc> + The exchange name is a client-selected string that identifies the exchange for + publish methods. + </doc> + <assert check = "length" value = "127" /> + <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]*$" /> + </domain> + + <domain name = "method-id" type = "short" /> + + <domain name = "no-ack" type = "bit" label = "no acknowledgement needed"> + <doc> + If this field is set the server does not expect acknowledgements for + messages. That is, when a message is delivered to the client the server + assumes the delivery will succeed and immediately dequeues it. This + functionality may increase performance but at the cost of reliability. + Messages can get lost if a client dies before they are delivered to the + application. + </doc> + </domain> + + <domain name = "no-local" type = "bit" label = "do not deliver own messages"> + <doc> + If the no-local field is set the server will not send messages to the connection that + published them. + </doc> + </domain> + + <domain name = "no-wait" type = "bit" label = "do not send reply method"> + <doc> + If set, the server will not respond to the method. The client should not wait + for a reply method. If the server could not complete the method it will raise a + channel or connection exception. + </doc> + </domain> + + <domain name = "path" type = "shortstr"> + <doc> + Unconstrained. + </doc> + <assert check = "notnull" /> + <assert check = "length" value = "127" /> + </domain> + + <domain name = "peer-properties" type = "table"> + <doc> + This table provides a set of peer properties, used for identification, debugging, + and general information. + </doc> + </domain> + + <domain name = "queue-name" type = "shortstr" label = "queue name"> + <doc> + The queue name identifies the queue within the vhost. In methods where the queue + name may be blank, and that has no specific significance, this refers to the + 'current' queue for the channel, meaning the last queue that the client declared + on the channel. If the client did not declare a queue, and the method needs a + queue name, this will result in a 502 (syntax error) channel exception. + </doc> + <assert check = "length" value = "127" /> + <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]*$" /> + </domain> + + <domain name = "redelivered" type = "bit" label = "message is being redelivered"> + <doc> + This indicates that the message has been previously delivered to this or + another client. + </doc> + <rule name = "implementation"> + <doc> + The server SHOULD try to signal redelivered messages when it can. When + redelivering a message that was not successfully acknowledged, the server + SHOULD deliver it to the original client if possible. + </doc> + <doc type = "scenario"> + Declare a shared queue and publish a message to the queue. Consume the + message using explicit acknowledgements, but do not acknowledge the + message. Close the connection, reconnect, and consume from the queue + again. The message should arrive with the redelivered flag set. + </doc> + </rule> + <rule name = "hinting"> + <doc> + The client MUST NOT rely on the redelivered field but should take it as a + hint that the message may already have been processed. A fully robust + client must be able to track duplicate received messages on non-transacted, + and locally-transacted channels. + </doc> + </rule> + </domain> + + <domain name = "message-count" type = "long" label = "number of messages in queue"> + <doc> + The number of messages in the queue, which will be zero for newly-declared + queues. This is the number of messages present in the queue, and committed + if the channel on which they were published is transacted, that are not + waiting acknowledgement. + </doc> + </domain> + + <domain name = "reply-code" type = "short" label = "reply code from server"> + <doc> + The reply code. The AMQ reply codes are defined as constants at the start + of this formal specification. + </doc> + <assert check = "notnull" /> + </domain> + + <domain name = "reply-text" type = "shortstr" label = "localised reply text"> + <doc> + The localised reply text. This text can be logged as an aid to resolving + issues. + </doc> + <assert check = "notnull" /> + </domain> + + <!-- Elementary domains --> + <domain name = "bit" type = "bit" label = "single bit" /> + <domain name = "octet" type = "octet" label = "single octet" /> + <domain name = "short" type = "short" label = "16-bit integer" /> + <domain name = "long" type = "long" label = "32-bit integer" /> + <domain name = "longlong" type = "longlong" label = "64-bit integer" /> + <domain name = "shortstr" type = "shortstr" label = "short string" /> + <domain name = "longstr" type = "longstr" label = "long string" /> + <domain name = "timestamp" type = "timestamp" label = "64-bit timestamp" /> + <domain name = "table" type = "table" label = "field table" /> + + <!-- == CONNECTION ======================================================= --> + + <class name = "connection" handler = "connection" index = "10" label = "work with socket connections"> + <doc> + The connection class provides methods for a client to establish a network connection to + a server, and for both peers to operate the connection thereafter. + </doc> + + <doc type = "grammar"> + connection = open-connection *use-connection close-connection + open-connection = C:protocol-header + S:START C:START-OK + *challenge + S:TUNE C:TUNE-OK + C:OPEN S:OPEN-OK + challenge = S:SECURE C:SECURE-OK + use-connection = *channel + close-connection = C:CLOSE S:CLOSE-OK + / S:CLOSE C:CLOSE-OK + </doc> + + <chassis name = "server" implement = "MUST" /> + <chassis name = "client" implement = "MUST" /> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "start" synchronous = "1" index = "10" label = "start connection negotiation"> + <doc> + This method starts the connection negotiation process by telling the client the + protocol version that the server proposes, along with a list of security mechanisms + which the client can use for authentication. + </doc> + + <rule name = "protocol-name"> + <doc> + If the server cannot support the protocol specified in the protocol header, + it MUST respond with a valid protocol header and then close the socket + connection. + </doc> + <doc type = "scenario"> + The client sends a protocol header containing an invalid protocol name. + The server MUST respond by sending a valid protocol header and then closing + the connection. + </doc> + </rule> + <rule name = "server-support"> + <doc> + The server MUST provide a protocol version that is lower than or equal to + that requested by the client in the protocol header. + </doc> + <doc type = "scenario"> + The client requests a protocol version that is higher than any valid + implementation, e.g. 2.0. The server must respond with a protocol header + indicating its supported protocol version, e.g. 1.0. + </doc> + </rule> + <rule name = "client-support"> + <doc> + If the client cannot handle the protocol version suggested by the server + it MUST close the socket connection without sending any further data. + </doc> + <doc type = "scenario"> + The server sends a protocol version that is lower than any valid + implementation, e.g. 0.1. The client must respond by closing the + connection without sending any further data. + </doc> + </rule> + + <chassis name = "client" implement = "MUST" /> + <response name = "start-ok" /> + + <field name = "version-major" domain = "octet" label = "protocol major version"> + <doc> + The major version number can take any value from 0 to 99 as defined in the + AMQP specification. + </doc> + </field> + + <field name = "version-minor" domain = "octet" label = "protocol minor version"> + <doc> + The minor version number can take any value from 0 to 99 as defined in the + AMQP specification. + </doc> + </field> + + <field name = "server-properties" domain = "peer-properties" label = "server properties"> + <rule name = "required-fields"> + <doc> + The properties SHOULD contain at least these fields: "host", specifying the + server host name or address, "product", giving the name of the server product, + "version", giving the name of the server version, "platform", giving the name + of the operating system, "copyright", if appropriate, and "information", giving + other general information. + </doc> + <doc type = "scenario"> + Client connects to server and inspects the server properties. It checks for + the presence of the required fields. + </doc> + </rule> + </field> + + <field name = "mechanisms" domain = "longstr" label = "available security mechanisms"> + <doc> + A list of the security mechanisms that the server supports, delimited by spaces. + </doc> + <assert check = "notnull" /> + </field> + + <field name = "locales" domain = "longstr" label = "available message locales"> + <doc> + A list of the message locales that the server supports, delimited by spaces. The + locale defines the language in which the server will send reply texts. + </doc> + <rule name = "required-support"> + <doc> + The server MUST support at least the en_US locale. + </doc> + <doc type = "scenario"> + Client connects to server and inspects the locales field. It checks for + the presence of the required locale(s). + </doc> + </rule> + <assert check = "notnull" /> + </field> + </method> + + <method name = "start-ok" synchronous = "1" index = "11" + label = "select security mechanism and locale"> + <doc> + This method selects a SASL security mechanism. + </doc> + + <chassis name = "server" implement = "MUST" /> + + <field name = "client-properties" domain = "peer-properties" label = "client properties"> + <rule name = "required-fields"> + <!-- This rule is not testable from the client side --> + <doc> + The properties SHOULD contain at least these fields: "product", giving the name + of the client product, "version", giving the name of the client version, "platform", + giving the name of the operating system, "copyright", if appropriate, and + "information", giving other general information. + </doc> + </rule> + </field> + + <field name = "mechanism" domain = "shortstr" label = "selected security mechanism"> + <doc> + A single security mechanisms selected by the client, which must be one of those + specified by the server. + </doc> + <rule name = "security"> + <doc> + The client SHOULD authenticate using the highest-level security profile it + can handle from the list provided by the server. + </doc> + </rule> + <rule name = "validity"> + <doc> + If the mechanism field does not contain one of the security mechanisms + proposed by the server in the Start method, the server MUST close the + connection without sending any further data. + </doc> + <doc type = "scenario"> + Client connects to server and sends an invalid security mechanism. The + server must respond by closing the connection (a socket close, with no + connection close negotiation). + </doc> + </rule> + <assert check = "notnull" /> + </field> + + <field name = "response" domain = "longstr" label = "security response data"> + <doc> + A block of opaque data passed to the security mechanism. The contents of this + data are defined by the SASL security mechanism. + </doc> + <assert check = "notnull" /> + </field> + + <field name = "locale" domain = "shortstr" label = "selected message locale"> + <doc> + A single message locale selected by the client, which must be one of those + specified by the server. + </doc> + <assert check = "notnull" /> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "secure" synchronous = "1" index = "20" label = "security mechanism challenge"> + <doc> + The SASL protocol works by exchanging challenges and responses until both peers have + received sufficient information to authenticate each other. This method challenges + the client to provide more information. + </doc> + + <chassis name = "client" implement = "MUST" /> + <response name = "secure-ok" /> + + <field name = "challenge" domain = "longstr" label = "security challenge data"> + <doc> + Challenge information, a block of opaque binary data passed to the security + mechanism. + </doc> + </field> + </method> + + <method name = "secure-ok" synchronous = "1" index = "21" label = "security mechanism response"> + <doc> + This method attempts to authenticate, passing a block of SASL data for the security + mechanism at the server side. + </doc> + + <chassis name = "server" implement = "MUST" /> + + <field name = "response" domain = "longstr" label = "security response data"> + <doc> + A block of opaque data passed to the security mechanism. The contents of this + data are defined by the SASL security mechanism. + </doc> + <assert check = "notnull" /> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "tune" synchronous = "1" index = "30" + label = "propose connection tuning parameters"> + <doc> + This method proposes a set of connection configuration values to the client. The + client can accept and/or adjust these. + </doc> + + <chassis name = "client" implement = "MUST" /> + + <response name = "tune-ok" /> + + <field name = "channel-max" domain = "short" label = "proposed maximum channels"> + <doc> + Specifies highest channel number that the server permits. Usable channel numbers + are in the range 1..channel-max. Zero indicates no specified limit. + </doc> + </field> + + <field name = "frame-max" domain = "long" label = "proposed maximum frame size"> + <doc> + The largest frame size that the server proposes for the connection, including + frame header and end-byte. The client can negotiate a lower value. Zero means + that the server does not impose any specific limit but may reject very large + frames if it cannot allocate resources for them. + </doc> + <rule name = "minimum"> + <doc> + Until the frame-max has been negotiated, both peers MUST accept frames of up + to frame-min-size octets large, and the minimum negotiated value for frame-max + is also frame-min-size. + </doc> + <doc type = "scenario"> + Client connects to server and sends a large properties field, creating a frame + of frame-min-size octets. The server must accept this frame. + </doc> + </rule> + </field> + + <field name = "heartbeat" domain = "short" label = "desired heartbeat delay"> + <doc> + The delay, in seconds, of the connection heartbeat that the server wants. + Zero means the server does not want a heartbeat. + </doc> + </field> + </method> + + <method name = "tune-ok" synchronous = "1" index = "31" + label = "negotiate connection tuning parameters"> + <doc> + This method sends the client's connection tuning parameters to the server. + Certain fields are negotiated, others provide capability information. + </doc> + + <chassis name = "server" implement = "MUST" /> + + <field name = "channel-max" domain = "short" label = "negotiated maximum channels"> + <doc> + The maximum total number of channels that the client will use per connection. + </doc> + <rule name = "upper-limit"> + <doc> + If the client specifies a channel max that is higher than the value provided + by the server, the server MUST close the connection without attempting a + negotiated close. The server may report the error in some fashion to assist + implementors. + </doc> + </rule> + <assert check = "notnull" /> + <assert check = "le" method = "tune" field = "channel-max" /> + </field> + + <field name = "frame-max" domain = "long" label = "negotiated maximum frame size"> + <doc> + The largest frame size that the client and server will use for the connection. + Zero means that the client does not impose any specific limit but may reject + very large frames if it cannot allocate resources for them. Note that the + frame-max limit applies principally to content frames, where large contents can + be broken into frames of arbitrary size. + </doc> + <rule name = "minimum"> + <doc> + Until the frame-max has been negotiated, both peers MUST accept frames of up + to frame-min-size octets large, and the minimum negotiated value for frame-max + is also frame-min-size. + </doc> + </rule> + <rule name = "upper-limit"> + <doc> + If the client specifies a frame max that is higher than the value provided + by the server, the server MUST close the connection without attempting a + negotiated close. The server may report the error in some fashion to assist + implementors. + </doc> + </rule> + </field> + + <field name = "heartbeat" domain = "short" label = "desired heartbeat delay"> + <doc> + The delay, in seconds, of the connection heartbeat that the client wants. Zero + means the client does not want a heartbeat. + </doc> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "open" synchronous = "1" index = "40" label = "open connection to virtual host"> + <doc> + This method opens a connection to a virtual host, which is a collection of + resources, and acts to separate multiple application domains within a server. + The server may apply arbitrary limits per virtual host, such as the number + of each type of entity that may be used, per connection and/or in total. + </doc> + + <chassis name = "server" implement = "MUST" /> + <response name = "open-ok" /> + + <field name = "virtual-host" domain = "path" label = "virtual host name"> + <doc> + The name of the virtual host to work with. + </doc> + <rule name = "separation"> + <doc> + If the server supports multiple virtual hosts, it MUST enforce a full + separation of exchanges, queues, and all associated entities per virtual + host. An application, connected to a specific virtual host, MUST NOT be able + to access resources of another virtual host. + </doc> + </rule> + <rule name = "security"> + <doc> + The server SHOULD verify that the client has permission to access the + specified virtual host. + </doc> + </rule> + </field> + <!-- Deprecated: "capabilities", must be zero --> + <field name = "reserved-1" type = "shortstr" reserved = "1" /> + <!-- Deprecated: "insist", must be zero --> + <field name = "reserved-2" type = "bit" reserved = "1" /> + </method> + + <method name = "open-ok" synchronous = "1" index = "41" label = "signal that connection is ready"> + <doc> + This method signals to the client that the connection is ready for use. + </doc> + <chassis name = "client" implement = "MUST" /> + <!-- Deprecated: "known-hosts", must be zero --> + <field name = "reserved-1" type = "shortstr" reserved = "1" /> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "close" synchronous = "1" index = "50" label = "request a connection close"> + <doc> + This method indicates that the sender wants to close the connection. This may be + due to internal conditions (e.g. a forced shut-down) or due to an error handling + a specific method, i.e. an exception. When a close is due to an exception, the + sender provides the class and method id of the method which caused the exception. + </doc> + <rule name = "stability"> + <doc> + After sending this method, any received methods except Close and Close-OK MUST + be discarded. The response to receiving a Close after sending Close must be to + send Close-Ok. + </doc> + </rule> + + <chassis name = "client" implement = "MUST" /> + <chassis name = "server" implement = "MUST" /> + <response name = "close-ok" /> + + <field name = "reply-code" domain = "reply-code" /> + <field name = "reply-text" domain = "reply-text" /> + + <field name = "class-id" domain = "class-id" label = "failing method class"> + <doc> + When the close is provoked by a method exception, this is the class of the + method. + </doc> + </field> + + <field name = "method-id" domain = "method-id" label = "failing method ID"> + <doc> + When the close is provoked by a method exception, this is the ID of the method. + </doc> + </field> + </method> + + <method name = "close-ok" synchronous = "1" index = "51" label = "confirm a connection close"> + <doc> + This method confirms a Connection.Close method and tells the recipient that it is + safe to release resources for the connection and close the socket. + </doc> + <rule name = "reporting"> + <doc> + A peer that detects a socket closure without having received a Close-Ok + handshake method SHOULD log the error. + </doc> + </rule> + <chassis name = "client" implement = "MUST" /> + <chassis name = "server" implement = "MUST" /> + </method> + </class> + + <!-- == CHANNEL ========================================================== --> + + <class name = "channel" handler = "channel" index = "20" label = "work with channels"> + <doc> + The channel class provides methods for a client to establish a channel to a + server and for both peers to operate the channel thereafter. + </doc> + + <doc type = "grammar"> + channel = open-channel *use-channel close-channel + open-channel = C:OPEN S:OPEN-OK + use-channel = C:FLOW S:FLOW-OK + / S:FLOW C:FLOW-OK + / functional-class + close-channel = C:CLOSE S:CLOSE-OK + / S:CLOSE C:CLOSE-OK + </doc> + + <chassis name = "server" implement = "MUST" /> + <chassis name = "client" implement = "MUST" /> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "open" synchronous = "1" index = "10" label = "open a channel for use"> + <doc> + This method opens a channel to the server. + </doc> + <rule name = "state" on-failure = "channel-error"> + <doc> + The client MUST NOT use this method on an already-opened channel. + </doc> + <doc type = "scenario"> + Client opens a channel and then reopens the same channel. + </doc> + </rule> + <chassis name = "server" implement = "MUST" /> + <response name = "open-ok" /> + <!-- Deprecated: "out-of-band", must be zero --> + <field name = "reserved-1" type = "shortstr" reserved = "1" /> + </method> + + <method name = "open-ok" synchronous = "1" index = "11" label = "signal that the channel is ready"> + <doc> + This method signals to the client that the channel is ready for use. + </doc> + <chassis name = "client" implement = "MUST" /> + <!-- Deprecated: "channel-id", must be zero --> + <field name = "reserved-1" type = "longstr" reserved = "1" /> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "flow" synchronous = "1" index = "20" label = "enable/disable flow from peer"> + <doc> + This method asks the peer to pause or restart the flow of content data sent by + a consumer. This is a simple flow-control mechanism that a peer can use to avoid + overflowing its queues or otherwise finding itself receiving more messages than + it can process. Note that this method is not intended for window control. It does + not affect contents returned by Basic.Get-Ok methods. + </doc> + + <rule name = "initial-state"> + <doc> + When a new channel is opened, it is active (flow is active). Some applications + assume that channels are inactive until started. To emulate this behaviour a + client MAY open the channel, then pause it. + </doc> + </rule> + + <rule name = "bidirectional"> + <doc> + When sending content frames, a peer SHOULD monitor the channel for incoming + methods and respond to a Channel.Flow as rapidly as possible. + </doc> + </rule> + + <rule name = "throttling"> + <doc> + A peer MAY use the Channel.Flow method to throttle incoming content data for + internal reasons, for example, when exchanging data over a slower connection. + </doc> + </rule> + + <rule name = "expected-behaviour"> + <doc> + The peer that requests a Channel.Flow method MAY disconnect and/or ban a peer + that does not respect the request. This is to prevent badly-behaved clients + from overwhelming a server. + </doc> + </rule> + + <chassis name = "server" implement = "MUST" /> + <chassis name = "client" implement = "MUST" /> + + <response name = "flow-ok" /> + + <field name = "active" domain = "bit" label = "start/stop content frames"> + <doc> + If 1, the peer starts sending content frames. If 0, the peer stops sending + content frames. + </doc> + </field> + </method> + + <method name = "flow-ok" index = "21" label = "confirm a flow method"> + <doc> + Confirms to the peer that a flow command was received and processed. + </doc> + <chassis name = "server" implement = "MUST" /> + <chassis name = "client" implement = "MUST" /> + <field name = "active" domain = "bit" label = "current flow setting"> + <doc> + Confirms the setting of the processed flow method: 1 means the peer will start + sending or continue to send content frames; 0 means it will not. + </doc> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "close" synchronous = "1" index = "40" label = "request a channel close"> + <doc> + This method indicates that the sender wants to close the channel. This may be due to + internal conditions (e.g. a forced shut-down) or due to an error handling a specific + method, i.e. an exception. When a close is due to an exception, the sender provides + the class and method id of the method which caused the exception. + </doc> + <rule name = "stability"> + <doc> + After sending this method, any received methods except Close and Close-OK MUST + be discarded. The response to receiving a Close after sending Close must be to + send Close-Ok. + </doc> + </rule> + + <chassis name = "client" implement = "MUST" /> + <chassis name = "server" implement = "MUST" /> + <response name = "close-ok" /> + + <field name = "reply-code" domain = "reply-code" /> + <field name = "reply-text" domain = "reply-text" /> + + <field name = "class-id" domain = "class-id" label = "failing method class"> + <doc> + When the close is provoked by a method exception, this is the class of the + method. + </doc> + </field> + + <field name = "method-id" domain = "method-id" label = "failing method ID"> + <doc> + When the close is provoked by a method exception, this is the ID of the method. + </doc> + </field> + </method> + + <method name = "close-ok" synchronous = "1" index = "41" label = "confirm a channel close"> + <doc> + This method confirms a Channel.Close method and tells the recipient that it is safe + to release resources for the channel. + </doc> + <rule name = "reporting"> + <doc> + A peer that detects a socket closure without having received a Channel.Close-Ok + handshake method SHOULD log the error. + </doc> + </rule> + <chassis name = "client" implement = "MUST" /> + <chassis name = "server" implement = "MUST" /> + </method> + </class> + + <!-- == EXCHANGE ========================================================= --> + + <class name = "exchange" handler = "channel" index = "40" label = "work with exchanges"> + <doc> + Exchanges match and distribute messages across queues. Exchanges can be configured in + the server or declared at runtime. + </doc> + + <doc type = "grammar"> + exchange = C:DECLARE S:DECLARE-OK + / C:DELETE S:DELETE-OK + </doc> + + <chassis name = "server" implement = "MUST" /> + <chassis name = "client" implement = "MUST" /> + + <rule name = "required-types"> + <doc> + The server MUST implement these standard exchange types: fanout, direct. + </doc> + <doc type = "scenario"> + Client attempts to declare an exchange with each of these standard types. + </doc> + </rule> + <rule name = "recommended-types"> + <doc> + The server SHOULD implement these standard exchange types: topic, headers. + </doc> + <doc type = "scenario"> + Client attempts to declare an exchange with each of these standard types. + </doc> + </rule> + <rule name = "required-instances"> + <doc> + The server MUST, in each virtual host, pre-declare an exchange instance + for each standard exchange type that it implements, where the name of the + exchange instance, if defined, is "amq." followed by the exchange type name. + </doc> + <doc> + The server MUST, in each virtual host, pre-declare at least two direct + exchange instances: one named "amq.direct", the other with no public name + that serves as a default exchange for Publish methods. + </doc> + <doc type = "scenario"> + Client declares a temporary queue and attempts to bind to each required + exchange instance ("amq.fanout", "amq.direct", "amq.topic", and "amq.headers" + if those types are defined). + </doc> + </rule> + <rule name = "default-exchange"> + <doc> + The server MUST pre-declare a direct exchange with no public name to act as + the default exchange for content Publish methods and for default queue bindings. + </doc> + <doc type = "scenario"> + Client checks that the default exchange is active by specifying a queue + binding with no exchange name, and publishing a message with a suitable + routing key but without specifying the exchange name, then ensuring that + the message arrives in the queue correctly. + </doc> + </rule> + <rule name = "default-access"> + <doc> + The server MUST NOT allow clients to access the default exchange except + by specifying an empty exchange name in the Queue.Bind and content Publish + methods. + </doc> + </rule> + <rule name = "extensions"> + <doc> + The server MAY implement other exchange types as wanted. + </doc> + </rule> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "declare" synchronous = "1" index = "10" label = "verify exchange exists, create if needed"> + <doc> + This method creates an exchange if it does not already exist, and if the exchange + exists, verifies that it is of the correct and expected class. + </doc> + <rule name = "minimum"> + <doc> + The server SHOULD support a minimum of 16 exchanges per virtual host and + ideally, impose no limit except as defined by available resources. + </doc> + <doc type = "scenario"> + The client declares as many exchanges as it can until the server reports + an error; the number of exchanges successfully declared must be at least + sixteen. + </doc> + </rule> + + <chassis name = "server" implement = "MUST" /> + <response name = "declare-ok" /> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "exchange" domain = "exchange-name"> + <rule name = "reserved" on-failure = "access-refused"> + <doc> + Exchange names starting with "amq." are reserved for pre-declared and + standardised exchanges. The client MAY declare an exchange starting with + "amq." if the passive option is set, or the exchange already exists. + </doc> + <doc type = "scenario"> + The client attempts to declare a non-existing exchange starting with + "amq." and with the passive option set to zero. + </doc> + </rule> + <rule name = "syntax" on-failure = "precondition-failed"> + <doc> + The exchange name consists of a non-empty sequence of these characters: + letters, digits, hyphen, underscore, period, or colon. + </doc> + <doc type = "scenario"> + The client attempts to declare an exchange with an illegal name. + </doc> + </rule> + <assert check = "notnull" /> + </field> + + <field name = "type" domain = "shortstr" label = "exchange type"> + <doc> + Each exchange belongs to one of a set of exchange types implemented by the + server. The exchange types define the functionality of the exchange - i.e. how + messages are routed through it. It is not valid or meaningful to attempt to + change the type of an existing exchange. + </doc> + <rule name = "typed" on-failure = "not-allowed"> + <doc> + Exchanges cannot be redeclared with different types. The client MUST not + attempt to redeclare an existing exchange with a different type than used + in the original Exchange.Declare method. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + <rule name = "support" on-failure = "command-invalid"> + <doc> + The client MUST NOT attempt to declare an exchange with a type that the + server does not support. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + </field> + + <field name = "passive" domain = "bit" label = "do not create exchange"> + <doc> + If set, the server will reply with Declare-Ok if the exchange already + exists with the same name, and raise an error if not. The client can + use this to check whether an exchange exists without modifying the + server state. When set, all other method fields except name and no-wait + are ignored. A declare with both passive and no-wait has no effect. + Arguments are compared for semantic equivalence. + </doc> + <rule name = "not-found"> + <doc> + If set, and the exchange does not already exist, the server MUST + raise a channel exception with reply code 404 (not found). + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + <rule name = "equivalent"> + <doc> + If not set and the exchange exists, the server MUST check that the + existing exchange has the same values for type, durable, and arguments + fields. The server MUST respond with Declare-Ok if the requested + exchange matches these fields, and MUST raise a channel exception if + not. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + </field> + + <field name = "durable" domain = "bit" label = "request a durable exchange"> + <doc> + If set when creating a new exchange, the exchange will be marked as durable. + Durable exchanges remain active when a server restarts. Non-durable exchanges + (transient exchanges) are purged if/when a server restarts. + </doc> + <rule name = "support"> + <doc> + The server MUST support both durable and transient exchanges. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + </field> + + <!-- Deprecated: "auto-delete", must be zero --> + <field name = "reserved-2" type = "bit" reserved = "1" /> + <!-- Deprecated: "internal", must be zero --> + <field name = "reserved-3" type = "bit" reserved = "1" /> + <field name = "no-wait" domain = "no-wait" /> + + <field name = "arguments" domain = "table" label = "arguments for declaration"> + <doc> + A set of arguments for the declaration. The syntax and semantics of these + arguments depends on the server implementation. + </doc> + </field> + </method> + + <method name = "declare-ok" synchronous = "1" index = "11" label = "confirm exchange declaration"> + <doc> + This method confirms a Declare method and confirms the name of the exchange, + essential for automatically-named exchanges. + </doc> + <chassis name = "client" implement = "MUST" /> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "delete" synchronous = "1" index = "20" label = "delete an exchange"> + <doc> + This method deletes an exchange. When an exchange is deleted all queue bindings on + the exchange are cancelled. + </doc> + + <chassis name = "server" implement = "MUST" /> + <response name = "delete-ok" /> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "exchange" domain = "exchange-name"> + <rule name = "exists" on-failure = "not-found"> + <doc> + The client MUST NOT attempt to delete an exchange that does not exist. + </doc> + </rule> + <assert check = "notnull" /> + </field> + + <field name = "if-unused" domain = "bit" label = "delete only if unused"> + <doc> + If set, the server will only delete the exchange if it has no queue bindings. If + the exchange has queue bindings the server does not delete it but raises a + channel exception instead. + </doc> + <rule name = "in-use" on-failure = "precondition-failed"> + <doc> + The server MUST NOT delete an exchange that has bindings on it, if the if-unused + field is true. + </doc> + <doc type = "scenario"> + The client declares an exchange, binds a queue to it, then tries to delete it + setting if-unused to true. + </doc> + </rule> + </field> + + <field name = "no-wait" domain = "no-wait" /> + </method> + + <method name = "delete-ok" synchronous = "1" index = "21" + label = "confirm deletion of an exchange"> + <doc>This method confirms the deletion of an exchange.</doc> + <chassis name = "client" implement = "MUST" /> + </method> + </class> + + <!-- == QUEUE ============================================================ --> + + <class name = "queue" handler = "channel" index = "50" label = "work with queues"> + <doc> + Queues store and forward messages. Queues can be configured in the server or created at + runtime. Queues must be attached to at least one exchange in order to receive messages + from publishers. + </doc> + + <doc type = "grammar"> + queue = C:DECLARE S:DECLARE-OK + / C:BIND S:BIND-OK + / C:UNBIND S:UNBIND-OK + / C:PURGE S:PURGE-OK + / C:DELETE S:DELETE-OK + </doc> + + <chassis name = "server" implement = "MUST" /> + <chassis name = "client" implement = "MUST" /> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "declare" synchronous = "1" index = "10" label = "declare queue, create if needed"> + <doc> + This method creates or checks a queue. When creating a new queue the client can + specify various properties that control the durability of the queue and its + contents, and the level of sharing for the queue. + </doc> + + <rule name = "default-binding"> + <doc> + The server MUST create a default binding for a newly-declared queue to the + default exchange, which is an exchange of type 'direct' and use the queue + name as the routing key. + </doc> + <doc type = "scenario"> + Client declares a new queue, and then without explicitly binding it to an + exchange, attempts to send a message through the default exchange binding, + i.e. publish a message to the empty exchange, with the queue name as routing + key. + </doc> + </rule> + + <rule name = "minimum-queues"> + <doc> + The server SHOULD support a minimum of 256 queues per virtual host and ideally, + impose no limit except as defined by available resources. + </doc> + <doc type = "scenario"> + Client attempts to declare as many queues as it can until the server reports + an error. The resulting count must at least be 256. + </doc> + </rule> + + <chassis name = "server" implement = "MUST" /> + <response name = "declare-ok" /> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "queue" domain = "queue-name"> + <rule name = "default-name"> + <doc> + The queue name MAY be empty, in which case the server MUST create a new + queue with a unique generated name and return this to the client in the + Declare-Ok method. + </doc> + <doc type = "scenario"> + Client attempts to declare several queues with an empty name. The client then + verifies that the server-assigned names are unique and different. + </doc> + </rule> + <rule name = "reserved" on-failure = "access-refused"> + <doc> + Queue names starting with "amq." are reserved for pre-declared and + standardised queues. The client MAY declare a queue starting with + "amq." if the passive option is set, or the queue already exists. + </doc> + <doc type = "scenario"> + The client attempts to declare a non-existing queue starting with + "amq." and with the passive option set to zero. + </doc> + </rule> + <rule name = "syntax" on-failure = "precondition-failed"> + <doc> + The queue name can be empty, or a sequence of these characters: + letters, digits, hyphen, underscore, period, or colon. + </doc> + <doc type = "scenario"> + The client attempts to declare a queue with an illegal name. + </doc> + </rule> + </field> + + <field name = "passive" domain = "bit" label = "do not create queue"> + <doc> + If set, the server will reply with Declare-Ok if the queue already + exists with the same name, and raise an error if not. The client can + use this to check whether a queue exists without modifying the + server state. When set, all other method fields except name and no-wait + are ignored. A declare with both passive and no-wait has no effect. + Arguments are compared for semantic equivalence. + </doc> + <rule name = "passive" on-failure = "not-found"> + <doc> + The client MAY ask the server to assert that a queue exists without + creating the queue if not. If the queue does not exist, the server + treats this as a failure. + </doc> + <doc type = "scenario"> + Client declares an existing queue with the passive option and expects + the server to respond with a declare-ok. Client then attempts to declare + a non-existent queue with the passive option, and the server must close + the channel with the correct reply-code. + </doc> + </rule> + <rule name = "equivalent"> + <doc> + If not set and the queue exists, the server MUST check that the + existing queue has the same values for durable, exclusive, auto-delete, + and arguments fields. The server MUST respond with Declare-Ok if the + requested queue matches these fields, and MUST raise a channel exception + if not. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + </field> + + <field name = "durable" domain = "bit" label = "request a durable queue"> + <doc> + If set when creating a new queue, the queue will be marked as durable. Durable + queues remain active when a server restarts. Non-durable queues (transient + queues) are purged if/when a server restarts. Note that durable queues do not + necessarily hold persistent messages, although it does not make sense to send + persistent messages to a transient queue. + </doc> + + <rule name = "persistence"> + <doc>The server MUST recreate the durable queue after a restart.</doc> + + <doc type = "scenario"> + Client declares a durable queue. The server is then restarted. The client + then attempts to send a message to the queue. The message should be successfully + delivered. + </doc> + </rule> + + <rule name = "types"> + <doc>The server MUST support both durable and transient queues.</doc> + <doc type = "scenario"> + A client declares two named queues, one durable and one transient. + </doc> + </rule> + </field> + + <field name = "exclusive" domain = "bit" label = "request an exclusive queue"> + <doc> + Exclusive queues may only be accessed by the current connection, and are + deleted when that connection closes. Passive declaration of an exclusive + queue by other connections are not allowed. + </doc> + + <rule name = "types"> + <doc> + The server MUST support both exclusive (private) and non-exclusive (shared) + queues. + </doc> + <doc type = "scenario"> + A client declares two named queues, one exclusive and one non-exclusive. + </doc> + </rule> + + <rule name = "exclusive" on-failure = "resource-locked"> + <doc> + The client MAY NOT attempt to use a queue that was declared as exclusive + by another still-open connection. + </doc> + <doc type = "scenario"> + One client declares an exclusive queue. A second client on a different + connection attempts to declare, bind, consume, purge, delete, or declare + a queue of the same name. + </doc> + </rule> + </field> + + <field name = "auto-delete" domain = "bit" label = "auto-delete queue when unused"> + <doc> + If set, the queue is deleted when all consumers have finished using it. The last + consumer can be cancelled either explicitly or because its channel is closed. If + there was no consumer ever on the queue, it won't be deleted. Applications can + explicitly delete auto-delete queues using the Delete method as normal. + </doc> + + <rule name = "pre-existence"> + <doc> + The server MUST ignore the auto-delete field if the queue already exists. + </doc> + <doc type = "scenario"> + Client declares two named queues, one as auto-delete and one explicit-delete. + Client then attempts to declare the two queues using the same names again, + but reversing the value of the auto-delete field in each case. Verify that the + queues still exist with the original auto-delete flag values. + </doc> + </rule> + </field> + + <field name = "no-wait" domain = "no-wait" /> + + <field name = "arguments" domain = "table" label = "arguments for declaration"> + <doc> + A set of arguments for the declaration. The syntax and semantics of these + arguments depends on the server implementation. + </doc> + </field> + </method> + + <method name = "declare-ok" synchronous = "1" index = "11" label = "confirms a queue definition"> + <doc> + This method confirms a Declare method and confirms the name of the queue, essential + for automatically-named queues. + </doc> + + <chassis name = "client" implement = "MUST" /> + + <field name = "queue" domain = "queue-name"> + <doc> + Reports the name of the queue. If the server generated a queue name, this field + contains that name. + </doc> + <assert check = "notnull" /> + </field> + + <field name = "message-count" domain = "message-count" /> + + <field name = "consumer-count" domain = "long" label = "number of consumers"> + <doc> + Reports the number of active consumers for the queue. Note that consumers can + suspend activity (Channel.Flow) in which case they do not appear in this count. + </doc> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "bind" synchronous = "1" index = "20" label = "bind queue to an exchange"> + <doc> + This method binds a queue to an exchange. Until a queue is bound it will not + receive any messages. In a classic messaging model, store-and-forward queues + are bound to a direct exchange and subscription queues are bound to a topic + exchange. + </doc> + + <rule name = "duplicates"> + <doc> + A server MUST allow ignore duplicate bindings - that is, two or more bind + methods for a specific queue, with identical arguments - without treating these + as an error. + </doc> + <doc type = "scenario"> + A client binds a named queue to an exchange. The client then repeats the bind + (with identical arguments). + </doc> + </rule> + + <rule name = "unique"> + <doc> + A server MUST not deliver the same message more than once to a queue, even if + the queue has multiple bindings that match the message. + </doc> + <doc type = "scenario"> + A client declares a named queue and binds it using multiple bindings to the + amq.topic exchange. The client then publishes a message that matches all its + bindings. + </doc> + </rule> + + <rule name = "transient-exchange"> + <doc> + The server MUST allow a durable queue to bind to a transient exchange. + </doc> + <doc type = "scenario"> + A client declares a transient exchange. The client then declares a named durable + queue and then attempts to bind the transient exchange to the durable queue. + </doc> + </rule> + + <rule name = "durable-exchange"> + <doc> + Bindings of durable queues to durable exchanges are automatically durable + and the server MUST restore such bindings after a server restart. + </doc> + <doc type = "scenario"> + A server declares a named durable queue and binds it to a durable exchange. The + server is restarted. The client then attempts to use the queue/exchange combination. + </doc> + </rule> + + <rule name = "binding-count"> + <doc> + The server SHOULD support at least 4 bindings per queue, and ideally, impose no + limit except as defined by available resources. + </doc> + <doc type = "scenario"> + A client declares a named queue and attempts to bind it to 4 different + exchanges. + </doc> + </rule> + + <chassis name = "server" implement = "MUST" /> + + <response name = "bind-ok" /> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "queue" domain = "queue-name"> + <doc>Specifies the name of the queue to bind.</doc> + <rule name = "queue-known" on-failure = "not-found"> + <doc> + The client MUST either specify a queue name or have previously declared a + queue on the same channel + </doc> + <doc type = "scenario"> + The client opens a channel and attempts to bind an unnamed queue. + </doc> + </rule> + <rule name = "must-exist" on-failure = "not-found"> + <doc> + The client MUST NOT attempt to bind a queue that does not exist. + </doc> + <doc type = "scenario"> + The client attempts to bind a non-existent queue. + </doc> + </rule> + </field> + + <field name = "exchange" domain = "exchange-name" label = "name of the exchange to bind to"> + <rule name = "exchange-existence" on-failure = "not-found"> + <doc> + A client MUST NOT be allowed to bind a queue to a non-existent exchange. + </doc> + <doc type = "scenario"> + A client attempts to bind an named queue to a undeclared exchange. + </doc> + </rule> + <rule name = "default-exchange"> + <doc> + The server MUST accept a blank exchange name to mean the default exchange. + </doc> + <doc type = "scenario"> + The client declares a queue and binds it to a blank exchange name. + </doc> + </rule> + </field> + + <field name = "routing-key" domain = "shortstr" label = "message routing key"> + <doc> + Specifies the routing key for the binding. The routing key is used for routing + messages depending on the exchange configuration. Not all exchanges use a + routing key - refer to the specific exchange documentation. If the queue name + is empty, the server uses the last queue declared on the channel. If the + routing key is also empty, the server uses this queue name for the routing + key as well. If the queue name is provided but the routing key is empty, the + server does the binding with that empty routing key. The meaning of empty + routing keys depends on the exchange implementation. + </doc> + <rule name = "direct-exchange-key-matching"> + <doc> + If a message queue binds to a direct exchange using routing key K and a + publisher sends the exchange a message with routing key R, then the message + MUST be passed to the message queue if K = R. + </doc> + </rule> + </field> + + <field name = "no-wait" domain = "no-wait" /> + + <field name = "arguments" domain = "table" label = "arguments for binding"> + <doc> + A set of arguments for the binding. The syntax and semantics of these arguments + depends on the exchange class. + </doc> + </field> + </method> + + <method name = "bind-ok" synchronous = "1" index = "21" label = "confirm bind successful"> + <doc>This method confirms that the bind was successful.</doc> + + <chassis name = "client" implement = "MUST" /> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "unbind" synchronous = "1" index = "50" label = "unbind a queue from an exchange"> + <doc>This method unbinds a queue from an exchange.</doc> + <rule name = "01"> + <doc>If a unbind fails, the server MUST raise a connection exception.</doc> + </rule> + <chassis name="server" implement="MUST"/> + <response name="unbind-ok"/> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "queue" domain = "queue-name"> + <doc>Specifies the name of the queue to unbind.</doc> + <rule name = "queue-known" on-failure = "not-found"> + <doc> + The client MUST either specify a queue name or have previously declared a + queue on the same channel + </doc> + <doc type = "scenario"> + The client opens a channel and attempts to unbind an unnamed queue. + </doc> + </rule> + <rule name = "must-exist" on-failure = "not-found"> + <doc> + The client MUST NOT attempt to unbind a queue that does not exist. + </doc> + <doc type = "scenario"> + The client attempts to unbind a non-existent queue. + </doc> + </rule> + </field> + + <field name = "exchange" domain = "exchange-name"> + <doc>The name of the exchange to unbind from.</doc> + <rule name = "must-exist" on-failure = "not-found"> + <doc> + The client MUST NOT attempt to unbind a queue from an exchange that + does not exist. + </doc> + <doc type = "scenario"> + The client attempts to unbind a queue from a non-existent exchange. + </doc> + </rule> + <rule name = "default-exchange"> + <doc> + The server MUST accept a blank exchange name to mean the default exchange. + </doc> + <doc type = "scenario"> + The client declares a queue and binds it to a blank exchange name. + </doc> + </rule> + </field> + + <field name = "routing-key" domain = "shortstr" label = "routing key of binding"> + <doc>Specifies the routing key of the binding to unbind.</doc> + </field> + + <field name = "arguments" domain = "table" label = "arguments of binding"> + <doc>Specifies the arguments of the binding to unbind.</doc> + </field> + </method> + + <method name = "unbind-ok" synchronous = "1" index = "51" label = "confirm unbind successful"> + <doc>This method confirms that the unbind was successful.</doc> + <chassis name = "client" implement = "MUST"/> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "purge" synchronous = "1" index = "30" label = "purge a queue"> + <doc> + This method removes all messages from a queue which are not awaiting + acknowledgment. + </doc> + + <rule name = "02"> + <doc> + The server MUST NOT purge messages that have already been sent to a client + but not yet acknowledged. + </doc> + </rule> + + <rule name = "03"> + <doc> + The server MAY implement a purge queue or log that allows system administrators + to recover accidentally-purged messages. The server SHOULD NOT keep purged + messages in the same storage spaces as the live messages since the volumes of + purged messages may get very large. + </doc> + </rule> + + <chassis name = "server" implement = "MUST" /> + + <response name = "purge-ok" /> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "queue" domain = "queue-name"> + <doc>Specifies the name of the queue to purge.</doc> + <rule name = "queue-known" on-failure = "not-found"> + <doc> + The client MUST either specify a queue name or have previously declared a + queue on the same channel + </doc> + <doc type = "scenario"> + The client opens a channel and attempts to purge an unnamed queue. + </doc> + </rule> + <rule name = "must-exist" on-failure = "not-found"> + <doc> + The client MUST NOT attempt to purge a queue that does not exist. + </doc> + <doc type = "scenario"> + The client attempts to purge a non-existent queue. + </doc> + </rule> + </field> + + <field name = "no-wait" domain = "no-wait" /> + </method> + + <method name = "purge-ok" synchronous = "1" index = "31" label = "confirms a queue purge"> + <doc>This method confirms the purge of a queue.</doc> + + <chassis name = "client" implement = "MUST" /> + + <field name = "message-count" domain = "message-count"> + <doc> + Reports the number of messages purged. + </doc> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "delete" synchronous = "1" index = "40" label = "delete a queue"> + <doc> + This method deletes a queue. When a queue is deleted any pending messages are sent + to a dead-letter queue if this is defined in the server configuration, and all + consumers on the queue are cancelled. + </doc> + + <rule name = "01"> + <doc> + The server SHOULD use a dead-letter queue to hold messages that were pending on + a deleted queue, and MAY provide facilities for a system administrator to move + these messages back to an active queue. + </doc> + </rule> + + <chassis name = "server" implement = "MUST" /> + + <response name = "delete-ok" /> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "queue" domain = "queue-name"> + <doc>Specifies the name of the queue to delete.</doc> + <rule name = "queue-known" on-failure = "not-found"> + <doc> + The client MUST either specify a queue name or have previously declared a + queue on the same channel + </doc> + <doc type = "scenario"> + The client opens a channel and attempts to delete an unnamed queue. + </doc> + </rule> + <rule name = "must-exist" on-failure = "not-found"> + <doc> + The client MUST NOT attempt to delete a queue that does not exist. + </doc> + <doc type = "scenario"> + The client attempts to delete a non-existent queue. + </doc> + </rule> + </field> + + <field name = "if-unused" domain = "bit" label = "delete only if unused"> + <doc> + If set, the server will only delete the queue if it has no consumers. If the + queue has consumers the server does does not delete it but raises a channel + exception instead. + </doc> + <rule name = "in-use" on-failure = "precondition-failed"> + <doc> + The server MUST NOT delete a queue that has consumers on it, if the if-unused + field is true. + </doc> + <doc type = "scenario"> + The client declares a queue, and consumes from it, then tries to delete it + setting if-unused to true. + </doc> + </rule> + </field> + + <field name = "if-empty" domain = "bit" label = "delete only if empty"> + <doc> + If set, the server will only delete the queue if it has no messages. + </doc> + <rule name = "not-empty" on-failure = "precondition-failed"> + <doc> + The server MUST NOT delete a queue that has messages on it, if the + if-empty field is true. + </doc> + <doc type = "scenario"> + The client declares a queue, binds it and publishes some messages into it, + then tries to delete it setting if-empty to true. + </doc> + </rule> + </field> + + <field name = "no-wait" domain = "no-wait" /> + </method> + + <method name = "delete-ok" synchronous = "1" index = "41" label = "confirm deletion of a queue"> + <doc>This method confirms the deletion of a queue.</doc> + + <chassis name = "client" implement = "MUST" /> + + <field name = "message-count" domain = "message-count"> + <doc>Reports the number of messages deleted.</doc> + </field> + </method> + </class> + + <!-- == BASIC ============================================================ --> + + <class name = "basic" handler = "channel" index = "60" label = "work with basic content"> + <doc> + The Basic class provides methods that support an industry-standard messaging model. + </doc> + + <doc type = "grammar"> + basic = C:QOS S:QOS-OK + / C:CONSUME S:CONSUME-OK + / C:CANCEL S:CANCEL-OK + / C:PUBLISH content + / S:RETURN content + / S:DELIVER content + / C:GET ( S:GET-OK content / S:GET-EMPTY ) + / C:ACK + / C:REJECT + / C:RECOVER-ASYNC + / C:RECOVER S:RECOVER-OK + </doc> + + <chassis name = "server" implement = "MUST" /> + <chassis name = "client" implement = "MAY" /> + + <rule name = "01"> + <doc> + The server SHOULD respect the persistent property of basic messages and + SHOULD make a best-effort to hold persistent basic messages on a reliable + storage mechanism. + </doc> + <doc type = "scenario"> + Send a persistent message to queue, stop server, restart server and then + verify whether message is still present. Assumes that queues are durable. + Persistence without durable queues makes no sense. + </doc> + </rule> + + <rule name = "02"> + <doc> + The server MUST NOT discard a persistent basic message in case of a queue + overflow. + </doc> + <doc type = "scenario"> + Declare a queue overflow situation with persistent messages and verify that + messages do not get lost (presumably the server will write them to disk). + </doc> + </rule> + + <rule name = "03"> + <doc> + The server MAY use the Channel.Flow method to slow or stop a basic message + publisher when necessary. + </doc> + <doc type = "scenario"> + Declare a queue overflow situation with non-persistent messages and verify + whether the server responds with Channel.Flow or not. Repeat with persistent + messages. + </doc> + </rule> + + <rule name = "04"> + <doc> + The server MAY overflow non-persistent basic messages to persistent + storage. + </doc> + <!-- Test scenario: untestable --> + </rule> + + <rule name = "05"> + <doc> + The server MAY discard or dead-letter non-persistent basic messages on a + priority basis if the queue size exceeds some configured limit. + </doc> + <!-- Test scenario: untestable --> + </rule> + + <rule name = "06"> + <doc> + The server MUST implement at least 2 priority levels for basic messages, + where priorities 0-4 and 5-9 are treated as two distinct levels. + </doc> + <doc type = "scenario"> + Send a number of priority 0 messages to a queue. Send one priority 9 + message. Consume messages from the queue and verify that the first message + received was priority 9. + </doc> + </rule> + + <rule name = "07"> + <doc> + The server MAY implement up to 10 priority levels. + </doc> + <doc type = "scenario"> + Send a number of messages with mixed priorities to a queue, so that all + priority values from 0 to 9 are exercised. A good scenario would be ten + messages in low-to-high priority. Consume from queue and verify how many + priority levels emerge. + </doc> + </rule> + + <rule name = "08"> + <doc> + The server MUST deliver messages of the same priority in order irrespective of + their individual persistence. + </doc> + <doc type = "scenario"> + Send a set of messages with the same priority but different persistence + settings to a queue. Consume and verify that messages arrive in same order + as originally published. + </doc> + </rule> + + <rule name = "09"> + <doc> + The server MUST support un-acknowledged delivery of Basic content, i.e. + consumers with the no-ack field set to TRUE. + </doc> + </rule> + + <rule name = "10"> + <doc> + The server MUST support explicitly acknowledged delivery of Basic content, + i.e. consumers with the no-ack field set to FALSE. + </doc> + <doc type = "scenario"> + Declare a queue and a consumer using explicit acknowledgements. Publish a + set of messages to the queue. Consume the messages but acknowledge only + half of them. Disconnect and reconnect, and consume from the queue. + Verify that the remaining messages are received. + </doc> + </rule> + + <!-- These are the properties for a Basic content --> + + <!-- MIME typing --> + <field name = "content-type" domain = "shortstr" label = "MIME content type" /> + <!-- MIME typing --> + <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" /> + <!-- For applications, and for header exchange routing --> + <field name = "headers" domain = "table" label = "message header field table" /> + <!-- For queues that implement persistence --> + <field name = "delivery-mode" domain = "octet" label = "non-persistent (1) or persistent (2)" /> + <!-- For queues that implement priorities --> + <field name = "priority" domain = "octet" label = "message priority, 0 to 9" /> + <!-- For application use, no formal behaviour --> + <field name = "correlation-id" domain = "shortstr" label = "application correlation identifier" /> + <!-- For application use, no formal behaviour but may hold the + name of a private response queue, when used in request messages --> + <field name = "reply-to" domain = "shortstr" label = "address to reply to" /> + <!-- For implementation use, no formal behaviour --> + <field name = "expiration" domain = "shortstr" label = "message expiration specification" /> + <!-- For application use, no formal behaviour --> + <field name = "message-id" domain = "shortstr" label = "application message identifier" /> + <!-- For application use, no formal behaviour --> + <field name = "timestamp" domain = "timestamp" label = "message timestamp" /> + <!-- For application use, no formal behaviour --> + <field name = "type" domain = "shortstr" label = "message type name" /> + <!-- For application use, no formal behaviour --> + <field name = "user-id" domain = "shortstr" label = "creating user id" /> + <!-- For application use, no formal behaviour --> + <field name = "app-id" domain = "shortstr" label = "creating application id" /> + <!-- Deprecated, was old cluster-id property --> + <field name = "reserved" domain = "shortstr" label = "reserved, must be empty" /> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service"> + <doc> + This method requests a specific quality of service. The QoS can be specified for the + current channel or for all channels on the connection. The particular properties and + semantics of a qos method always depend on the content class semantics. Though the + qos method could in principle apply to both peers, it is currently meaningful only + for the server. + </doc> + + <chassis name = "server" implement = "MUST" /> + <response name = "qos-ok" /> + + <field name = "prefetch-size" domain = "long" label = "prefetch window in octets"> + <doc> + The client can request that messages be sent in advance so that when the client + finishes processing a message, the following message is already held locally, + rather than needing to be sent down the channel. Prefetching gives a performance + improvement. This field specifies the prefetch window size in octets. The server + will send a message in advance if it is equal to or smaller in size than the + available prefetch size (and also falls into other prefetch limits). May be set + to zero, meaning "no specific limit", although other prefetch limits may still + apply. The prefetch-size is ignored if the no-ack option is set. + </doc> + <rule name = "01"> + <doc> + The server MUST ignore this setting when the client is not processing any + messages - i.e. the prefetch size does not limit the transfer of single + messages to a client, only the sending in advance of more messages while + the client still has one or more unacknowledged messages. + </doc> + <doc type = "scenario"> + Define a QoS prefetch-size limit and send a single message that exceeds + that limit. Verify that the message arrives correctly. + </doc> + </rule> + </field> + + <field name = "prefetch-count" domain = "short" label = "prefetch window in messages"> + <doc> + Specifies a prefetch window in terms of whole messages. This field may be used + in combination with the prefetch-size field; a message will only be sent in + advance if both prefetch windows (and those at the channel and connection level) + allow it. The prefetch-count is ignored if the no-ack option is set. + </doc> + <rule name = "01"> + <doc> + The server may send less data in advance than allowed by the client's + specified prefetch windows but it MUST NOT send more. + </doc> + <doc type = "scenario"> + Define a QoS prefetch-size limit and a prefetch-count limit greater than + one. Send multiple messages that exceed the prefetch size. Verify that + no more than one message arrives at once. + </doc> + </rule> + </field> + + <field name = "global" domain = "bit" label = "apply to entire connection"> + <doc> + By default the QoS settings apply to the current channel only. If this field is + set, they are applied to the entire connection. + </doc> + </field> + </method> + + <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos"> + <doc> + This method tells the client that the requested QoS levels could be handled by the + server. The requested QoS applies to all active consumers until a new QoS is + defined. + </doc> + <chassis name = "client" implement = "MUST" /> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "consume" synchronous = "1" index = "20" label = "start a queue consumer"> + <doc> + This method asks the server to start a "consumer", which is a transient request for + messages from a specific queue. Consumers last as long as the channel they were + declared on, or until the client cancels them. + </doc> + + <rule name = "01"> + <doc> + The server SHOULD support at least 16 consumers per queue, and ideally, impose + no limit except as defined by available resources. + </doc> + <doc type = "scenario"> + Declare a queue and create consumers on that queue until the server closes the + connection. Verify that the number of consumers created was at least sixteen + and report the total number. + </doc> + </rule> + + <chassis name = "server" implement = "MUST" /> + <response name = "consume-ok" /> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "queue" domain = "queue-name"> + <doc>Specifies the name of the queue to consume from.</doc> + </field> + + <field name = "consumer-tag" domain = "consumer-tag"> + <doc> + Specifies the identifier for the consumer. The consumer tag is local to a + channel, so two clients can use the same consumer tags. If this field is + empty the server will generate a unique tag. + </doc> + <rule name = "01" on-failure = "not-allowed"> + <doc> + The client MUST NOT specify a tag that refers to an existing consumer. + </doc> + <doc type = "scenario"> + Attempt to create two consumers with the same non-empty tag, on the + same channel. + </doc> + </rule> + <rule name = "02" on-failure = "not-allowed"> + <doc> + The consumer tag is valid only within the channel from which the + consumer was created. I.e. a client MUST NOT create a consumer in one + channel and then use it in another. + </doc> + <doc type = "scenario"> + Attempt to create a consumer in one channel, then use in another channel, + in which consumers have also been created (to test that the server uses + unique consumer tags). + </doc> + </rule> + </field> + + <field name = "no-local" domain = "no-local" /> + + <field name = "no-ack" domain = "no-ack" /> + + <field name = "exclusive" domain = "bit" label = "request exclusive access"> + <doc> + Request exclusive consumer access, meaning only this consumer can access the + queue. + </doc> + + <rule name = "01" on-failure = "access-refused"> + <doc> + The client MAY NOT gain exclusive access to a queue that already has + active consumers. + </doc> + <doc type = "scenario"> + Open two connections to a server, and in one connection declare a shared + (non-exclusive) queue and then consume from the queue. In the second + connection attempt to consume from the same queue using the exclusive + option. + </doc> + </rule> + </field> + + <field name = "no-wait" domain = "no-wait" /> + + <field name = "arguments" domain = "table" label = "arguments for declaration"> + <doc> + A set of arguments for the consume. The syntax and semantics of these + arguments depends on the server implementation. + </doc> + </field> + </method> + + <method name = "consume-ok" synchronous = "1" index = "21" label = "confirm a new consumer"> + <doc> + The server provides the client with a consumer tag, which is used by the client + for methods called on the consumer at a later stage. + </doc> + <chassis name = "client" implement = "MUST" /> + <field name = "consumer-tag" domain = "consumer-tag"> + <doc> + Holds the consumer tag specified by the client or provided by the server. + </doc> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "cancel" synchronous = "1" index = "30" label = "end a queue consumer"> + <doc> + This method cancels a consumer. This does not affect already delivered + messages, but it does mean the server will not send any more messages for + that consumer. The client may receive an arbitrary number of messages in + between sending the cancel method and receiving the cancel-ok reply. + </doc> + + <rule name = "01"> + <doc> + If the queue does not exist the server MUST ignore the cancel method, so + long as the consumer tag is valid for that channel. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + + <chassis name = "server" implement = "MUST" /> + <response name = "cancel-ok" /> + + <field name = "consumer-tag" domain = "consumer-tag" /> + <field name = "no-wait" domain = "no-wait" /> + </method> + + <method name = "cancel-ok" synchronous = "1" index = "31" label = "confirm a cancelled consumer"> + <doc> + This method confirms that the cancellation was completed. + </doc> + <chassis name = "client" implement = "MUST" /> + <field name = "consumer-tag" domain = "consumer-tag" /> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "publish" content = "1" index = "40" label = "publish a message"> + <doc> + This method publishes a message to a specific exchange. The message will be routed + to queues as defined by the exchange configuration and distributed to any active + consumers when the transaction, if any, is committed. + </doc> + + <chassis name = "server" implement = "MUST" /> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "exchange" domain = "exchange-name"> + <doc> + Specifies the name of the exchange to publish to. The exchange name can be + empty, meaning the default exchange. If the exchange name is specified, and that + exchange does not exist, the server will raise a channel exception. + </doc> + + <rule name = "must-exist" on-failure = "not-found"> + <doc> + The client MUST NOT attempt to publish a content to an exchange that + does not exist. + </doc> + <doc type = "scenario"> + The client attempts to publish a content to a non-existent exchange. + </doc> + </rule> + <rule name = "default-exchange"> + <doc> + The server MUST accept a blank exchange name to mean the default exchange. + </doc> + <doc type = "scenario"> + The client declares a queue and binds it to a blank exchange name. + </doc> + </rule> + <rule name = "02"> + <doc> + If the exchange was declared as an internal exchange, the server MUST raise + a channel exception with a reply code 403 (access refused). + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + + <rule name = "03"> + <doc> + The exchange MAY refuse basic content in which case it MUST raise a channel + exception with reply code 540 (not implemented). + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + </field> + + <field name = "routing-key" domain = "shortstr" label = "Message routing key"> + <doc> + Specifies the routing key for the message. The routing key is used for routing + messages depending on the exchange configuration. + </doc> + </field> + + <field name = "mandatory" domain = "bit" label = "indicate mandatory routing"> + <doc> + This flag tells the server how to react if the message cannot be routed to a + queue. If this flag is set, the server will return an unroutable message with a + Return method. If this flag is zero, the server silently drops the message. + </doc> + + <rule name = "01"> + <doc> + The server SHOULD implement the mandatory flag. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + </field> + + <field name = "immediate" domain = "bit" label = "request immediate delivery"> + <doc> + This flag tells the server how to react if the message cannot be routed to a + queue consumer immediately. If this flag is set, the server will return an + undeliverable message with a Return method. If this flag is zero, the server + will queue the message, but with no guarantee that it will ever be consumed. + </doc> + + <rule name = "01"> + <doc> + The server SHOULD implement the immediate flag. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + </field> + </method> + + <method name = "return" content = "1" index = "50" label = "return a failed message"> + <doc> + This method returns an undeliverable message that was published with the "immediate" + flag set, or an unroutable message published with the "mandatory" flag set. The + reply code and text provide information about the reason that the message was + undeliverable. + </doc> + + <chassis name = "client" implement = "MUST" /> + + <field name = "reply-code" domain = "reply-code" /> + <field name = "reply-text" domain = "reply-text" /> + + <field name = "exchange" domain = "exchange-name"> + <doc> + Specifies the name of the exchange that the message was originally published + to. May be empty, meaning the default exchange. + </doc> + </field> + + <field name = "routing-key" domain = "shortstr" label = "Message routing key"> + <doc> + Specifies the routing key name specified when the message was published. + </doc> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "deliver" content = "1" index = "60" + label = "notify the client of a consumer message"> + <doc> + This method delivers a message to the client, via a consumer. In the asynchronous + message delivery model, the client starts a consumer using the Consume method, then + the server responds with Deliver methods as and when messages arrive for that + consumer. + </doc> + + <rule name = "01"> + <doc> + The server SHOULD track the number of times a message has been delivered to + clients and when a message is redelivered a certain number of times - e.g. 5 + times - without being acknowledged, the server SHOULD consider the message to be + unprocessable (possibly causing client applications to abort), and move the + message to a dead letter queue. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + + <chassis name = "client" implement = "MUST" /> + + <field name = "consumer-tag" domain = "consumer-tag" /> + <field name = "delivery-tag" domain = "delivery-tag" /> + <field name = "redelivered" domain = "redelivered" /> + + <field name = "exchange" domain = "exchange-name"> + <doc> + Specifies the name of the exchange that the message was originally published to. + May be empty, indicating the default exchange. + </doc> + </field> + + <field name = "routing-key" domain = "shortstr" label = "Message routing key"> + <doc>Specifies the routing key name specified when the message was published.</doc> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "get" synchronous = "1" index = "70" label = "direct access to a queue"> + <doc> + This method provides a direct access to the messages in a queue using a synchronous + dialogue that is designed for specific types of application where synchronous + functionality is more important than performance. + </doc> + + <response name = "get-ok" /> + <response name = "get-empty" /> + <chassis name = "server" implement = "MUST" /> + + <!-- Deprecated: "ticket", must be zero --> + <field name = "reserved-1" type = "short" reserved = "1" /> + + <field name = "queue" domain = "queue-name"> + <doc>Specifies the name of the queue to get a message from.</doc> + </field> + <field name = "no-ack" domain = "no-ack" /> + </method> + + <method name = "get-ok" synchronous = "1" content = "1" index = "71" + label = "provide client with a message"> + <doc> + This method delivers a message to the client following a get method. A message + delivered by 'get-ok' must be acknowledged unless the no-ack option was set in the + get method. + </doc> + + <chassis name = "client" implement = "MAY" /> + + <field name = "delivery-tag" domain = "delivery-tag" /> + <field name = "redelivered" domain = "redelivered" /> + <field name = "exchange" domain = "exchange-name"> + <doc> + Specifies the name of the exchange that the message was originally published to. + If empty, the message was published to the default exchange. + </doc> + </field> + + <field name = "routing-key" domain = "shortstr" label = "Message routing key"> + <doc>Specifies the routing key name specified when the message was published.</doc> + </field> + + <field name = "message-count" domain = "message-count" /> + </method> + + <method name = "get-empty" synchronous = "1" index = "72" + label = "indicate no messages available"> + <doc> + This method tells the client that the queue has no messages available for the + client. + </doc> + <chassis name = "client" implement = "MAY" /> + <!-- Deprecated: "cluster-id", must be empty --> + <field name = "reserved-1" type = "shortstr" reserved = "1" /> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "ack" index = "80" label = "acknowledge one or more messages"> + <doc> + This method acknowledges one or more messages delivered via the Deliver or Get-Ok + methods. The client can ask to confirm a single message or a set of messages up to + and including a specific message. + </doc> + + <chassis name = "server" implement = "MUST" /> + + <field name = "delivery-tag" domain = "delivery-tag" /> + <field name = "multiple" domain = "bit" label = "acknowledge multiple messages"> + <doc> + If set to 1, the delivery tag is treated as "up to and including", so that the + client can acknowledge multiple messages with a single method. If set to zero, + the delivery tag refers to a single message. If the multiple field is 1, and the + delivery tag is zero, tells the server to acknowledge all outstanding messages. + </doc> + <rule name = "exists" on-failure = "precondition-failed"> + <doc> + The server MUST validate that a non-zero delivery-tag refers to a delivered + message, and raise a channel exception if this is not the case. On a transacted + channel, this check MUST be done immediately and not delayed until a Tx.Commit. + Specifically, a client MUST not acknowledge the same message more than once. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "reject" index = "90" label = "reject an incoming message"> + <doc> + This method allows a client to reject a message. It can be used to interrupt and + cancel large incoming messages, or return untreatable messages to their original + queue. + </doc> + + <rule name = "01"> + <doc> + The server SHOULD be capable of accepting and process the Reject method while + sending message content with a Deliver or Get-Ok method. I.e. the server should + read and process incoming methods while sending output frames. To cancel a + partially-send content, the server sends a content body frame of size 1 (i.e. + with no data except the frame-end octet). + </doc> + </rule> + + <rule name = "02"> + <doc> + The server SHOULD interpret this method as meaning that the client is unable to + process the message at this time. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + + <rule name = "03"> + <doc> + The client MUST NOT use this method as a means of selecting messages to process. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + + <chassis name = "server" implement = "MUST" /> + + <field name = "delivery-tag" domain = "delivery-tag" /> + + <field name = "requeue" domain = "bit" label = "requeue the message"> + <doc> + If requeue is true, the server will attempt to requeue the message. If requeue + is false or the requeue attempt fails the messages are discarded or dead-lettered. + </doc> + + <rule name = "01"> + <doc> + The server MUST NOT deliver the message to the same client within the + context of the current channel. The recommended strategy is to attempt to + deliver the message to an alternative consumer, and if that is not possible, + to move the message to a dead-letter queue. The server MAY use more + sophisticated tracking to hold the message on the queue and redeliver it to + the same client at a later stage. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "recover-async" index = "100" label = "redeliver unacknowledged messages" + deprecated = "1"> + <doc> + This method asks the server to redeliver all unacknowledged messages on a + specified channel. Zero or more messages may be redelivered. This method + is deprecated in favour of the synchronous Recover/Recover-Ok. + </doc> + <rule name = "01"> + <doc> + The server MUST set the redelivered flag on all messages that are resent. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + <chassis name = "server" implement = "MAY" /> + <field name = "requeue" domain = "bit" label = "requeue the message"> + <doc> + If this field is zero, the message will be redelivered to the original + recipient. If this bit is 1, the server will attempt to requeue the message, + potentially then delivering it to an alternative subscriber. + </doc> + </field> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "recover" index = "110" label = "redeliver unacknowledged messages"> + <doc> + This method asks the server to redeliver all unacknowledged messages on a + specified channel. Zero or more messages may be redelivered. This method + replaces the asynchronous Recover. + </doc> + <rule name = "01"> + <doc> + The server MUST set the redelivered flag on all messages that are resent. + </doc> + <doc type = "scenario"> + TODO. + </doc> + </rule> + <chassis name = "server" implement = "MUST" /> + <field name = "requeue" domain = "bit" label = "requeue the message"> + <doc> + If this field is zero, the message will be redelivered to the original + recipient. If this bit is 1, the server will attempt to requeue the message, + potentially then delivering it to an alternative subscriber. + </doc> + </field> + </method> + + <method name = "recover-ok" synchronous = "1" index = "111" label = "confirm recovery"> + <doc> + This method acknowledges a Basic.Recover method. + </doc> + <chassis name = "client" implement = "MUST" /> + </method> + </class> + + <!-- == TX =============================================================== --> + + <class name = "tx" handler = "channel" index = "90" label = "work with transactions"> + <doc> + The Tx class allows publish and ack operations to be batched into atomic + units of work. The intention is that all publish and ack requests issued + within a transaction will complete successfully or none of them will. + Servers SHOULD implement atomic transactions at least where all publish + or ack requests affect a single queue. Transactions that cover multiple + queues may be non-atomic, given that queues can be created and destroyed + asynchronously, and such events do not form part of any transaction. + Further, the behaviour of transactions with respect to the immediate and + mandatory flags on Basic.Publish methods is not defined. + </doc> + + <rule name = "not multiple queues"> + <doc> + Applications MUST NOT rely on the atomicity of transactions that + affect more than one queue. + </doc> + </rule> + <rule name = "not immediate"> + <doc> + Applications MUST NOT rely on the behaviour of transactions that + include messages published with the immediate option. + </doc> + </rule> + <rule name = "not mandatory"> + <doc> + Applications MUST NOT rely on the behaviour of transactions that + include messages published with the mandatory option. + </doc> + </rule> + + <doc type = "grammar"> + tx = C:SELECT S:SELECT-OK + / C:COMMIT S:COMMIT-OK + / C:ROLLBACK S:ROLLBACK-OK + </doc> + + <chassis name = "server" implement = "SHOULD" /> + <chassis name = "client" implement = "MAY" /> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "select" synchronous = "1" index = "10" label = "select standard transaction mode"> + <doc> + This method sets the channel to use standard transactions. The client must use this + method at least once on a channel before using the Commit or Rollback methods. + </doc> + <chassis name = "server" implement = "MUST" /> + <response name = "select-ok" /> + </method> + + <method name = "select-ok" synchronous = "1" index = "11" label = "confirm transaction mode"> + <doc> + This method confirms to the client that the channel was successfully set to use + standard transactions. + </doc> + <chassis name = "client" implement = "MUST" /> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "commit" synchronous = "1" index = "20" label = "commit the current transaction"> + <doc> + This method commits all message publications and acknowledgments performed in + the current transaction. A new transaction starts immediately after a commit. + </doc> + <chassis name = "server" implement = "MUST" /> + <response name = "commit-ok" /> + + <rule name = "transacted" on-failure = "precondition-failed"> + <doc> + The client MUST NOT use the Commit method on non-transacted channels. + </doc> + <doc type = "scenario"> + The client opens a channel and then uses Tx.Commit. + </doc> + </rule> + </method> + + <method name = "commit-ok" synchronous = "1" index = "21" label = "confirm a successful commit"> + <doc> + This method confirms to the client that the commit succeeded. Note that if a commit + fails, the server raises a channel exception. + </doc> + <chassis name = "client" implement = "MUST" /> + </method> + + <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name = "rollback" synchronous = "1" index = "30" + label = "abandon the current transaction"> + <doc> + This method abandons all message publications and acknowledgments performed in + the current transaction. A new transaction starts immediately after a rollback. + Note that unacked messages will not be automatically redelivered by rollback; + if that is required an explicit recover call should be issued. + </doc> + <chassis name = "server" implement = "MUST" /> + <response name = "rollback-ok" /> + + <rule name = "transacted" on-failure = "precondition-failed"> + <doc> + The client MUST NOT use the Rollback method on non-transacted channels. + </doc> + <doc type = "scenario"> + The client opens a channel and then uses Tx.Rollback. + </doc> + </rule> + </method> + + <method name = "rollback-ok" synchronous = "1" index = "31" label = "confirm successful rollback"> + <doc> + This method confirms to the client that the rollback succeeded. Note that if an + rollback fails, the server raises a channel exception. + </doc> + <chassis name = "client" implement = "MUST" /> + </method> + </class> + +</amqp> diff --git a/setup.py b/setup.py index fe3b9ae87d3a24da0152382bc308d61695e80e82..220c7eaba287e8f1e68975ed38c3ab6214cab6b9 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,8 @@ setup(name='CoolAMQP', keywords=['amqp', 'pyamqp', 'rabbitmq', 'client', 'network', 'ha', 'high availability'], packages=[ 'coolamqp', - 'coolamqp.backends' + 'coolamqp.backends', + 'coolamqp.framing', ], license='MIT License', long_description=u'The AMQP client that handles reconnection madness for you', diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9f2b35b38d89264ee25685611d0a65a192e165f6 --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1,2 @@ +# coding=UTF-8 +from __future__ import absolute_import, division, print_function diff --git a/utils/compdefs.py b/utils/compdefs.py new file mode 100644 index 0000000000000000000000000000000000000000..eb07d284aae5ea31c83023a3ba8ce9c4b9d0dfcb --- /dev/null +++ b/utils/compdefs.py @@ -0,0 +1,228 @@ +from xml.etree import ElementTree +import collections +import struct +import six + +from getp import get_constants, get_classes, get_domains, byname, name_class, name_method, name_field + +def frepr(p): + if isinstance(p, basestring): + p = six.text_type(p) + s = repr(p) + + if isinstance(p, basestring) and not s.startswith('u'): + return 'u' + s + else: + return s + +def normname(p): + return p.strip().replace('-', '_').upper() + +def infertype(p): + try: + return int(p) + except ValueError: + return p + +def doxify(label, doc, prefix=4, blank=True): # output a full docstring section + label = [] if label is None else [label] + doc = [] if doc is None else doc.split(u'\n') + pre = u' '*prefix + + doc = label + doc + + if len(doc) == 0: + return u'\n' + + doc[0] = doc[0].capitalize() + + if len(doc) == 1: + return pre + doc[0] + u'\n' + + if blank: + doc = [doc[0], u''] + doc[1:] + + f = (u'\n'.join(pre + lin for lin in doc))[prefix:] + print(repr(f)) + return f + +def ffmt(data, *args, **kwargs): + for arg in args: + op = str if kwargs.get('sane', True) else frepr + data = data.replace('%s', op(arg), 1) + return data + +def compile_definitions(xml_file='resources/amqp0-9-1.xml', out_file='coolamqp/framing/definitions.py'): + """parse resources/amqp-0-9-1.xml into """ + + xml = ElementTree.parse(xml_file) + + with open(out_file, 'wb') as out: + + out.write('''# coding=UTF-8 +from __future__ import print_function, absolute_import +""" +Constants used in AMQP protocol. + +Generated automatically by CoolAMQP from AMQP machine-readable specification. +See utils/compdefs.py for the tool + +AMQP is copyright (c) 2016 OASIS +CoolAMQP is copyright (c) 2016 DMS Serwis s.c. +""" + +''') + + def line(data, *args, **kwargs): + out.write(ffmt(data, *args, sane=True)) + + # Output core ones + line('# Core frame types\n') + for constant in get_constants(xml): + g = ffmt('%s = %s', constant.name.strip().replace('-', '_').upper(), constant.value) + line(g) + if constant.docs: + lines = constant.docs.split('\n') + line(' # %s\n', lines[0]) + if len(lines) > 1: + for ln in lines[1:]: + line(u' '*len(g)) + line(u' # %s\n', ln) + else: + line('\n') + + # get domains + domain_to_basic_type = {} + line('DOMAIN_TO_BASIC_TYPE = {\n') + for domain in get_domains(xml): + line(u' %s: %s,\n', frepr(domain.name), frepr(None if domain.elementary else domain.type)) + if not domain.elementary: + domain_to_basic_type[domain.name] = domain.type + + line('}\n') + + line(''' + +class AMQPClass(object): + pass + + +class AMQPMethod(object): + RESPONSE_TO = None + REPLY_WITH = [] + + +''') + + # Output classes + for cls in get_classes(xml): + line('''class %s(AMQPClass): + """ + %s + """ + NAME = %s + INDEX = %s + +''', name_class(cls.name), doxify(None, cls.docs), frepr(cls.name), cls.index) + + for method in cls.methods: + + + line('''\nclass %s%s(AMQPMethod): + """ + %s + """ + CLASS = %s + NAME = %s + CLASSNAME = %s + CLASS_INDEX = %s + METHOD_INDEX = %s + FULLNAME = %s + SYNCHRONOUS = %s + REPLY_WITH = [%s] +''', + name_class(cls.name), name_method(method.name), + doxify(method.label, method.docs), + name_class(cls.name), + frepr(method.name), + frepr(cls.name), + frepr(cls.index), + frepr(method.index), + frepr(cls.name + '.' + method.name), + repr(method.synchronous), + str(', '.join([name_class(cls.name)+name_method(kidname) for kidname in method.response])), + ) + + # Am I a response somewhere? + for paren in cls.methods: + if method.name in paren.response: + line(' RESPONSE_TO = %s%s\n', name_class(cls.name), name_method(paren.name)) + + # fields + line(' FIELDS = [') + for field in method.fields: + tp = field.type + while tp in domain_to_basic_type: + tp = domain_to_basic_type[tp] + + line('\n (%s, %s, %s), ', frepr(field.name), frepr(field.type), frepr(tp)) + if field.label: + line(' # '+field.label) + + line('\n ]\n\n') + + non_reserved_fields = [field for field in method.fields if not field.reserved] + + # constructor + line(''' def __init__(%s): + """ + Create frame %s + +''', + u', '.join(['self'] + [name_field(field.name) for field in non_reserved_fields]), + cls.name + '.' + method.name, + ) + + for field in non_reserved_fields: + tp = field.type + while tp in domain_to_basic_type: + tp = domain_to_basic_type[tp] + + if (field.label is not None) or (field.docs is not None): + line(' :param %s: %s\n', name_field(field.name), + doxify(field.label, field.docs, prefix=12, blank=False)) + line(' :type %s: %s (as %s)\n', name_field(field.name), field.type, tp) + + line(' """\n') + + for field in non_reserved_fields: + line(' self.%s = %s\n', name_field(field.name), name_field(field.name)) + + # end + line('''\n def to_frame(self): + """ + Return self as bytes + + :return: AMQP frame payload + """ + raise NotImplementedError() + +''') + + + + # Get me a dict - (classid, methodid) => class of method + dct = {} + for cls in get_classes(xml): + for method in cls.methods: + dct[((cls.index, method.index))] = '%s%s' % (name_class(cls.name), name_method(method.name)) + + line('\nIDENT_TO_METHOD = {\n') + for k, v in dct.items(): + line(' %s: %s,\n', repr(k), v) + line('}\n\n') + + + +if __name__ == '__main__': + compile_definitions() diff --git a/utils/getp.py b/utils/getp.py new file mode 100644 index 0000000000000000000000000000000000000000..ad26d826c008092012e3f08cdafdb5f2cb87ff12 --- /dev/null +++ b/utils/getp.py @@ -0,0 +1,94 @@ +# coding=UTF-8 +from __future__ import absolute_import, division, print_function +from collections import namedtuple +import six + +# docs may be None + +Constant = namedtuple('Constant', ('name', 'value', 'kind', 'docs')) # kind is AMQP constant class # value is int +Field = namedtuple('Field', ('name', 'type', 'label', 'docs', 'reserved')) # reserved is bool +Method = namedtuple('Method', ('name', 'synchronous', 'index', 'label', 'docs', 'fields', 'response')) + # synchronous is bool + # repponse is a list of method.name +Class_ = namedtuple('Class_', ('name', 'index', 'docs', 'methods')) # label is int +Domain = namedtuple('Domain', ('name', 'type', 'elementary')) # elementary is bool + + +def get_docs(elem): + for kid in elem.getchildren(): + + if kid.tag == 'rule': + return get_docs(kid) + + s = kid.text.strip().split('\n') + return u'\n'.join([u.strip() for u in s if len(u.strip()) > 0]) + + return None + + +def for_domain(elem): + a = elem.attrib + return Domain(six.text_type(a['name']), a['type'], a['type'] == a['name']) + + +def for_method_field(elem): # for <field> in <method> + a = elem.attrib + return Field(six.text_type(a['name']), a['domain'] if 'domain' in a else a['type'], + a.get('label', None), + get_docs(elem), + a.get('reserved', '0') == '1') + + +def for_method(elem): # for <method> + a = elem.attrib + return Method(six.text_type(a['name']), bool(int(a.get('synchronous', '0'))), int(a['index']), a['label'], get_docs(elem), + [for_method_field(fie) for fie in elem.getchildren() if fie.tag == 'field'], + [e.attrib['name'] for e in elem.findall('response')] + ) + +def for_class(elem): # for <class> + a = elem.attrib + methods = sorted([for_method(me) for me in elem.getchildren() if me.tag == 'method'], key=lambda m: (m.name.strip('-')[0], -len(m.response))) + return Class_(six.text_type(a['name']), int(a['index']), get_docs(elem) or a['label'], methods) + +def for_constant(elem): # for <constant> + a = elem.attrib + return Constant(a['name'], int(a['value']), a.get('class', ''), get_docs(elem)) + + +def get_constants(xml): + return [for_constant(e) for e in xml.findall('constant')] + +def get_classes(xml): + return [for_class(e) for e in xml.findall('class')] + +def get_domains(xml): + return [for_domain(e) for e in xml.findall('domain')] + + +def a_text(callable): + def roll(*args, **kwargs): + return six.text_type(callable(*args, **kwargs)) + return roll + +def byname(list_of_things): + return dict((a.name, a) for a in list_of_things) + +@a_text +def name_class(classname): + """Change AMQP class name to Python class name""" + return classname.capitalize() + +@a_text +def name_method(methodname): + if '-' in methodname: + i = methodname.find('-') + return methodname[0:i].capitalize() + methodname[i+1].upper() + methodname[i+2:] + else: + return methodname.capitalize() + +@a_text +def name_field(field): + if field in ('global', ): + field = field + '_' + return field.replace('-', '_')