Beispiel #1
0
 def _parseHead(self):
     try:
         endOfHead = self._findHead(self._data, self._start).end()
     except AttributeError:
         return
     command, rawHeaders = None, []
     for line in self._data[self._start:endOfHead].decode(self._codec).split(StompSpec.LINE_DELIMITER):
         if line[-1:] == self._stripLineDelimiter:
             line = line[:-1]
         if command is None:
             command = line
             if command not in self._commands:
                 self._raise('Invalid command (version %s): %r' % (self.version, command))
             _unescape = unescape(self.version, command)
             continue
         if not line:
             break
         try:
             name, value = line.split(StompSpec.HEADER_SEPARATOR, 1)
         except ValueError:
             self._raise('No separator in header line: %r' % line)
         rawHeaders.append((_unescape(name), _unescape(value)))
     self._frame = StompFrame(command=command, rawHeaders=rawHeaders, version=self.version)
     self._start = endOfHead
     try:
         self._eof = self._seek = self._start + int(self._frame.headers[StompSpec.CONTENT_LENGTH_HEADER])
     except KeyError:
         pass
     return True
Beispiel #2
0
def disconnect(receipt=None, version=None):
    """Create a **DISCONNECT** frame.
    
    :param receipt: Add a **receipt** header with this id to request a **RECEIPT** frame from the broker. If :obj:`None`, no such header is added.
    """
    headers = {}
    frame = StompFrame(StompSpec.DISCONNECT, headers, version=version)
    _addReceiptHeader(frame, receipt)
    return frame
Beispiel #3
0
def commit(transaction, receipt=None, version=None):
    """Create a **COMMIT** frame.
    
    :param transaction: The id of the transaction.
    :param receipt: See :meth:`disconnect`.
    """
    frame = StompFrame(StompSpec.COMMIT,
                       {StompSpec.TRANSACTION_HEADER: transaction},
                       version=version)
    _addReceiptHeader(frame, receipt)
    return frame
Beispiel #4
0
def ack(frame, transactions=None, receipt=None):
    """Create an **ACK** frame for a received **MESSAGE** frame.
    
    :param frame: The :class:`~.frame.StompFrame` object representing the **MESSAGE** frame we wish to ack.
    :param transactions: The ids of currently active transactions --- only if the **frame** is part of one of these transactions, the **transaction** header is included in the ACK frame.
    :param receipt: See :func:`disconnect`.
    """
    frame = StompFrame(StompSpec.ACK,
                       _ackHeaders(frame, transactions),
                       version=frame.version)
    _addReceiptHeader(frame, receipt)
    return frame
Beispiel #5
0
def send(destination, body=b'', headers=None, receipt=None, version=None):
    """Create a **SEND** frame.
    
    :param destination: Destination for the frame.
    :param body: Binary message body. If the body contains null-bytes, it must be accompanied by the STOMP header **content-length** which specifies the number of bytes in the message body.
    :param headers: Additional STOMP headers.
    :param receipt: See :func:`disconnect`.
    """
    frame = StompFrame(StompSpec.SEND,
                       dict(headers or []),
                       body,
                       version=version)
    frame.headers[StompSpec.DESTINATION_HEADER] = destination
    _addReceiptHeader(frame, receipt)
    return frame
Beispiel #6
0
def nack(frame, transactions=None, receipt=None):
    """Create a **NACK** frame for a received **MESSAGE** frame.
    
    :param frame: The :class:`~.frame.StompFrame` object representing the **MESSAGE** frame we wish to nack.
    :param transactions: The ids of currently active transactions --- only if the **frame** is part of one of these transactions, the **transaction** header is included in the NACK frame.
    :param receipt: See :func:`disconnect`.
    """
    version = frame.version
    if version == StompSpec.VERSION_1_0:
        raise StompProtocolError('%s not supported (version %s)' %
                                 (StompSpec.NACK, version))
    frame = StompFrame(StompSpec.NACK,
                       _ackHeaders(frame, transactions),
                       version=frame.version)
    _addReceiptHeader(frame, receipt)
    return frame
Beispiel #7
0
def unsubscribe(token, receipt=None, version=None):
    """Create an **UNSUBSCRIBE** frame.
    
    :param token: The result of the :func:`subscribe` command which you used to initiate the subscription in question.
    :param receipt: See :meth:`disconnect`.
    """
    version = _version(version)
    frame = StompFrame(StompSpec.UNSUBSCRIBE, dict([token]), version=version)
    _addReceiptHeader(frame, receipt)
    try:
        _checkHeader(frame, StompSpec.ID_HEADER)
    except StompProtocolError:
        if version != StompSpec.VERSION_1_0:
            raise
        _checkHeader(frame, StompSpec.DESTINATION_HEADER)
    return frame
Beispiel #8
0
def connect(login=None,
            passcode=None,
            headers=None,
            versions=None,
            host=None,
            heartBeats=None):
    """Create a **CONNECT** frame.
    
    :param login: The **login** header. The default is :obj:`None`, which means that no such header will be added.
    :param passcode: The **passcode** header. The default is :obj:`None`, which means that no such header will be added.
    :param headers: Additional STOMP headers.
    :param versions: A list of the STOMP versions we wish to support. The default is :obj:`None`, which means that we will offer the broker to accept any version prior or equal to the default STOMP protocol version.
    :param host: The **host** header which gives this client a human readable name on the broker side.
    :param heartBeats: A pair (client heart-beat, server heart-beat) of integer heart-beat intervals in ms. Both intervals must be non-negative. A client heart-beat of 0 means that no heart-beats will be sent by the client. Similarly, a server heart-beat of 0 means that the client does not expect heart-beats from the server.
    """
    headers = dict(headers or [])
    if login is not None:
        headers[StompSpec.LOGIN_HEADER] = login
    if passcode is not None:
        headers[StompSpec.PASSCODE_HEADER] = passcode
    versions = [StompSpec.VERSION_1_0] if (versions is None) else list(
        sorted(_version(v) for v in versions))
    if versions != [StompSpec.VERSION_1_0]:
        headers[StompSpec.ACCEPT_VERSION_HEADER] = ','.join(
            _version(version) for version in versions)
        if host is None:
            host = ''
        headers[StompSpec.HOST_HEADER] = host
    if heartBeats:
        if versions == [StompSpec.VERSION_1_0]:
            raise StompProtocolError(
                'Heart-beating not supported (version %s)' %
                StompSpec.VERSION_1_0)
        try:
            heartBeats = tuple(int(t) for t in heartBeats)
            if not all(t >= 0 for t in heartBeats):
                raise
            heartBeats = '%d,%d' % heartBeats
        except:
            raise StompProtocolError(
                'Invalid heart-beats (two non-negative integers required): %s'
                % str(heartBeats))
        headers[StompSpec.HEART_BEAT_HEADER] = heartBeats

    return StompFrame(StompSpec.CONNECT, headers)
Beispiel #9
0
def stomp(login=None,
          passcode=None,
          headers=None,
          versions=None,
          host=None,
          heartBeats=None):
    """Create a **STOMP** frame. Not supported in STOMP protocol 1.0, synonymous to :func:`connect` for STOMP protocol 1.1 and higher.
    """
    if (versions is None) or (list(versions) == [StompSpec.VERSION_1_0]):
        raise StompProtocolError('Unsupported command (version %s): %s' %
                                 (StompSpec.VERSION_1_0, StompSpec.STOMP))
    frame = connect(login=login,
                    passcode=passcode,
                    headers=headers,
                    versions=versions,
                    host=host,
                    heartBeats=heartBeats)
    return StompFrame(StompSpec.STOMP, frame.headers, frame.body)
Beispiel #10
0
def subscribe(destination, headers, receipt=None, version=None):
    """Create a pair (frame, token) of a **SUBSCRIBE** frame and a token which you have to keep if you wish to match incoming **MESSAGE** frames to this subscription  with :func:`message` or to :func:`unsubscribe` later.
    
    :param destination: Destination for the subscription.
    :param headers: Additional STOMP headers.
    :param receipt: See :func:`disconnect`.
    """
    version = _version(version)
    frame = StompFrame(StompSpec.SUBSCRIBE,
                       dict(headers or []),
                       version=version)
    frame.headers[StompSpec.DESTINATION_HEADER] = destination
    _addReceiptHeader(frame, receipt)
    subscription = None
    try:
        subscription = _checkHeader(frame, StompSpec.ID_HEADER)
    except StompProtocolError:
        if (version != StompSpec.VERSION_1_0):
            raise
    token = (StompSpec.DESTINATION_HEADER,
             destination) if (subscription is None) else (StompSpec.ID_HEADER,
                                                          subscription)
    return frame, tuple(map(textType, token))
Beispiel #11
0
"""This module implements a low-level and stateless API for all commands of the STOMP protocol version supported by stompest. All STOMP command frames are represented as :class:`~.frame.StompFrame` objects. It forms the basis for :class:`~.session.StompSession` which represents the full state of an abstract STOMP protocol session and (via :class:`~.session.StompSession`) of both high-level STOMP clients. You can use the commands API independently of other stompest modules to roll your own STOMP related functionality.

.. note :: Whenever you have to pass a **version** parameter to a command, this is because the behavior of that command depends on the STOMP protocol version of your current session. The default version is the value of :attr:`StompSpec.DEFAULT_VERSION`, which is currently :obj:`'1.0'` but may change in upcoming versions of stompest (or you might override it yourself). Any command which does not conform to the STOMP protocol version in question will result in a :class:`~.error.StompProtocolError`. The **version** parameter will always be the last argument in the signature; since command signatures may vary with a new STOMP protocol version, you are advised to always specify it as a keyword (as opposed to a positional) argument.

Examples:

>>> from stompest.protocol import commands, StompFrame, StompSpec
>>> versions = list(commands.versions(StompSpec.VERSION_1_1))
>>> versions
['1.0', '1.1']
>>> commands.connect(versions=versions)
StompFrame(command='CONNECT', headers={'host': '', 'accept-version': '1.0,1.1'})
>>> frame, token = commands.subscribe('/queue/test', {StompSpec.ACK_HEADER: 'client-individual', 'activemq.prefetchSize': '100'})
>>> frame = StompFrame(StompSpec.MESSAGE, {StompSpec.DESTINATION_HEADER: '/queue/test', StompSpec.MESSAGE_ID_HEADER: '007'}, b'hello')
>>> frame
StompFrame(command='MESSAGE', headers={'destination': '/queue/test', 'message-id': '007'}, body=b'hello')
>>> commands.message(frame) == token # This message matches your subscription.
True
>>> commands.message(frame)
('destination', '/queue/test')
>>> frame.version = StompSpec.VERSION_1_1
>>> commands.message(frame)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
stompest.error.StompProtocolError: Invalid MESSAGE frame (subscription header mandatory in version 1.1) [headers={'destination': '/queue/test', 'message-id': '007'}]
>>> commands.disconnect(receipt='message-12345')
StompFrame(command='DISCONNECT', headers={'receipt': 'message-12345'})

.. seealso :: Specification of STOMP protocols `1.0 <http://stomp.github.com//stomp-specification-1.0.html>`_ and `1.1 <http://stomp.github.com//stomp-specification-1.1.html>`_, your favorite broker's documentation for additional STOMP headers.
"""