class Stomp(object): """STOMP Client. :param hostname: Hostname of the STOMP server to connect to. :param port: The port to use. (default ``61613``) """ ConnectionError = ConnectionError ConnectionTimeoutError = ConnectionTimeoutError NotConnectedError = NotConnectedError def __init__(self, hostname, port=61613): self.host = hostname self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._subscribed_to = {} self._subscribed = None self._callback = None self.connected = None self.frame = Frame() def connect(self, username=None, password=None, clientid=None): """Connect to STOMP server. :keyword username: Username for connection :keyword password: Password for connection :keyword clientid: Client identification for persistent connections """ try: self.sock.connect((self.host, self.port)) self.frame.connect(self.sock, username=username, password=password, clientid=clientid) except socket.timeout, exc: raise self.ConnectionTimeoutError(*exc.args) except socket.error, exc: raise self.ConnectionError(*exc.args)
def should_send_frame_and_return_frame(self): my_frame = Frame() my_frame._getline = Dingus() headers = {'destination': '/queue/nose_test', 'persistent': 'true'} body = {'body': 'Testing'} my_frame._getline.return_value = \ 'CONNECTED\nsession:ID:nose-session123\n\n\x00\n' my_frame.connect(self.sockobj.connect('localhost', 99999)) this_frame = my_frame.build_frame({'command': 'SEND', 'headers': headers, 'body': body}, want_receipt=True) my_frame._getline.return_value = \ 'RECEIPT\nreceipt-id:ID:nose-receipt123\n\n\x00\n' send_frame = my_frame.send_frame(this_frame.as_string()) assert isinstance(my_frame, Frame)
def should_send_frame_and_return_frame(self): my_frame = Frame() my_frame._getline = Dingus() headers = {'destination': '/queue/nose_test', 'persistent': 'true'} body = {'body': 'Testing'} my_frame._getline.return_value = \ 'CONNECTED\nsession:ID:nose-session123\n\n\x00\n' my_frame.connect(self.sockobj.connect('localhost', 99999)) this_frame = my_frame.build_frame( { 'command': 'SEND', 'headers': headers, 'body': body }, want_receipt=True) my_frame._getline.return_value = \ 'RECEIPT\nreceipt-id:ID:nose-receipt123\n\n\x00\n' send_frame = my_frame.send_frame(this_frame.as_string()) assert isinstance(my_frame, Frame)
class WhenSendingFrames(DingusTestCase(Frame)): def setup(self): super(WhenSendingFrames, self).setup() self.frame = Frame() self.sockobj = frame.socket.socket(socket.AF_INET, socket.SOCK_STREAM) def should_send_frame_and_return_none(self): self.frame._getline = Dingus() self.frame._getline.return_value = \ 'CONNECTED\nsession:ID:nose-session123\n\n\x00\n' self.frame.connect(self.sockobj.connect('localhost', 99999)) this_frame = self.frame.build_frame({ 'command': 'CONNECT', 'headers': {} }) send_frame = self.frame.send_frame(this_frame.as_string()) assert send_frame is None def should_send_frame_and_return_frame(self): my_frame = Frame() my_frame._getline = Dingus() headers = {'destination': '/queue/nose_test', 'persistent': 'true'} body = {'body': 'Testing'} my_frame._getline.return_value = \ 'CONNECTED\nsession:ID:nose-session123\n\n\x00\n' my_frame.connect(self.sockobj.connect('localhost', 99999)) this_frame = my_frame.build_frame( { 'command': 'SEND', 'headers': headers, 'body': body }, want_receipt=True) my_frame._getline.return_value = \ 'RECEIPT\nreceipt-id:ID:nose-receipt123\n\n\x00\n' send_frame = my_frame.send_frame(this_frame.as_string()) assert isinstance(my_frame, Frame)
class WhenSendingFrames(DingusTestCase(Frame)): def setup(self): super(WhenSendingFrames, self).setup() self.frame = Frame() self.sockobj = frame.socket.socket(socket.AF_INET, socket.SOCK_STREAM) def should_send_frame_and_return_none(self): self.frame._getline = Dingus() self.frame._getline.return_value = \ 'CONNECTED\nsession:ID:nose-session123\n\n\x00\n' self.frame.connect(self.sockobj.connect('localhost', 99999)) this_frame = self.frame.build_frame({'command': 'CONNECT', 'headers': {}}) send_frame = self.frame.send_frame(this_frame.as_string()) assert send_frame is None def should_send_frame_and_return_frame(self): my_frame = Frame() my_frame._getline = Dingus() headers = {'destination': '/queue/nose_test', 'persistent': 'true'} body = {'body': 'Testing'} my_frame._getline.return_value = \ 'CONNECTED\nsession:ID:nose-session123\n\n\x00\n' my_frame.connect(self.sockobj.connect('localhost', 99999)) this_frame = my_frame.build_frame({'command': 'SEND', 'headers': headers, 'body': body}, want_receipt=True) my_frame._getline.return_value = \ 'RECEIPT\nreceipt-id:ID:nose-receipt123\n\n\x00\n' send_frame = my_frame.send_frame(this_frame.as_string()) assert isinstance(my_frame, Frame)
class WhenSettingUp(DingusTestCase(Frame)): def setup(self): super(WhenSettingUp, self).setup() self.frame = Frame() self.sockobj = frame.socket.socket(socket.AF_INET, socket.SOCK_STREAM) def should_set_name(self): assert frame.socket.calls('gethostbyname', frame.socket.gethostname()) def should_connect(self): self.frame._getline = Dingus() self.frame._getline.return_value = \ 'CONNECTED\nsession:ID:nose-session123\n\n\x00\n' self.frame.connect(self.sockobj.connect('localhost', 99999)) sendall = self.frame.sock.calls('sendall', DontCare).one().args[0] assert self.frame.session is not None assert 'CONNECT' in sendall def should_connect_with_authentication(self): self.frame._getline = Dingus() self.frame._getline.return_value = \ 'CONNECTED\nsession:ID:nose-session123\n\n\x00\n' self.frame.connect(self.sockobj.connect('localhost', 99999), username="******", password="******") sendall = self.frame.sock.calls('sendall', DontCare).one().args[0] assert self.frame.session is not None assert 'login:test' in sendall def should_set_client_id(self): self.frame._getline = Dingus() self.frame._getline.return_value = \ 'CONNECTED\nsession:ID:nose-session123\n\n\x00\n' self.frame.connect(self.sockobj.connect('localhost', 99999), clientid="test") sendall = self.frame.sock.calls('sendall', DontCare).one().args[0] assert self.frame.session is not None assert 'client-id:test' in sendall
class WhenParsingFrames(DingusTestCase(Frame, exclude=["UnknownBrokerResponseError", "BrokerErrorResponse"])): def setup(self): super(WhenParsingFrames, self).setup() self.frame = Frame() self.sockobj = frame.socket.socket(socket.AF_INET, socket.SOCK_STREAM) def should_parse_headers(self): header = 'destination:/queue/nose_test' parsed = self.frame.parse_headers(header) assert isinstance(parsed, type({})) def should_parse_command(self): command_str = 'CONNECT\nsession:ID:nose-session123' command = self.frame.parse_command(command_str) assert isinstance(command, type('')) def should_set_bytes_message(self): my_frame = Frame() my_frame._getline = Dingus() body = 'Test 1' my_frame._getline.return_value = ( 'MESSAGE\nsession:ID:nose-session123' '\ncontent-length:%d\n\n%s\x00\n' % (len(body), body)) this_frame = my_frame.parse_frame() assert 'bytes_message' in this_frame.headers def should_get_line(self): command = 'CONNECTED' headers = {'session': 'ID:nose-session123'} body = '\x00' my_frame = Frame() self.frame.parse_frame = Dingus() this_frame = my_frame.build_frame({'command': command, 'headers': headers, 'body': body}) self.frame.parse_frame.return_value = this_frame self.frame.connect(self.sockobj.connect(('localhost', 99999))) header = "session:%(session)s\n" % headers ret = '\n'.join([command, header, body]) self.frame.sock.recv.return_value = ret self.frame._getline() assert self.frame.sock.calls('recv', 1) def should_not_get_line(self): my_frame = Frame() my_frame._getline = Dingus() my_frame._getline.return_value = None ret_value = my_frame.parse_frame() assert ret_value is None def should_fail_to_get_headers(self): my_frame = Frame() my_frame._getline = Dingus() my_frame._getline.return_value = \ 'RECEIPTreceipt-id:ID:nose-receipt123' nose_tools.assert_raises(UnknownBrokerResponseError, my_frame.parse_frame) def should_get_error_from_broker(self): my_frame = Frame() my_frame._getline = Dingus() command = 'ERROR' header = 'message:Illegal command' body = 'Error Message' my_frame._getline.return_value = \ '%s\n%s\ncontent-length:%d\n\n%s\n\x00' % (command, header, len(body), body) nose_tools.assert_raises(BrokerErrorResponse, my_frame.parse_frame) def should_return_frame_repr(self): my_frame = Frame() assert isinstance(repr(my_frame), type(''))
class WhenParsingFrames( DingusTestCase( Frame, exclude=["UnknownBrokerResponseError", "BrokerErrorResponse"])): def setup(self): super(WhenParsingFrames, self).setup() self.frame = Frame() self.sockobj = frame.socket.socket(socket.AF_INET, socket.SOCK_STREAM) def should_parse_headers(self): header = 'destination:/queue/nose_test' parsed = self.frame.parse_headers(header) assert isinstance(parsed, type({})) def should_parse_command(self): command_str = 'CONNECT\nsession:ID:nose-session123' command = self.frame.parse_command(command_str) assert isinstance(command, type('')) def should_set_bytes_message(self): my_frame = Frame() my_frame._getline = Dingus() body = 'Test 1' my_frame._getline.return_value = ('MESSAGE\nsession:ID:nose-session123' '\ncontent-length:%d\n\n%s\x00\n' % (len(body), body)) this_frame = my_frame.parse_frame() assert 'bytes_message' in this_frame.headers def should_get_line(self): command = 'CONNECTED' headers = {'session': 'ID:nose-session123'} body = '\x00' my_frame = Frame() self.frame.parse_frame = Dingus() this_frame = my_frame.build_frame({ 'command': command, 'headers': headers, 'body': body }) self.frame.parse_frame.return_value = this_frame self.frame.connect(self.sockobj.connect(('localhost', 99999))) header = "session:%(session)s\n" % headers ret = '\n'.join([command, header, body]) self.frame.sock.recv.return_value = ret self.frame._getline() assert self.frame.sock.calls('recv', 1) def should_not_get_line(self): my_frame = Frame() my_frame._getline = Dingus() my_frame._getline.return_value = None ret_value = my_frame.parse_frame() assert ret_value is None def should_fail_to_get_headers(self): my_frame = Frame() my_frame._getline = Dingus() my_frame._getline.return_value = \ 'RECEIPTreceipt-id:ID:nose-receipt123' nose_tools.assert_raises(UnknownBrokerResponseError, my_frame.parse_frame) def should_get_error_from_broker(self): my_frame = Frame() my_frame._getline = Dingus() command = 'ERROR' header = 'message:Illegal command' body = 'Error Message' my_frame._getline.return_value = \ '%s\n%s\ncontent-length:%d\n\n%s\n\x00' % (command, header, len(body), body) nose_tools.assert_raises(BrokerErrorResponse, my_frame.parse_frame) def should_return_frame_repr(self): my_frame = Frame() assert isinstance(repr(my_frame), type(''))
class Stomp(object): """STOMP Client. :param hostname: Hostname of the STOMP server to connect to. :param port: The port to use. (default ``61613``) """ ConnectionError = ConnectionError ConnectionTimeoutError = ConnectionTimeoutError NotConnectedError = NotConnectedError def __init__(self, hostname, port=61613): self.host = hostname self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._subscribed_to = {} self._subscribed = None self._callback = None self.connected = None self.frame = Frame() def connect(self, username=None, password=None, clientid=None): """Connect to STOMP server. :keyword username: Username for connection :keyword password: Password for connection :keyword clientid: Client identification for persistent connections """ try: self.sock.connect((self.host, self.port)) self.frame.connect(self.sock, username=username, password=password, clientid=clientid) except socket.timeout as exc: raise self.ConnectionTimeoutError(*exc.args) except socket.error as exc: raise self.ConnectionError(*exc.args) self.connected = True def disconnect(self, conf=None): """Disconnect from the server.""" try: for destination in self._subscribed_to.keys(): self.unsubscribe({"destination": destination}) self._send_command("DISCONNECT", conf) except self.NotConnectedError: pass try: self.sock.shutdown(0) self.sock.close() except socket.error as exc: # likely wasn't connected pass self.connected = False def send(self, conf=None, receipt=True): """Send message to STOMP server You'll need to pass the body and any other headers your STOMP server likes. destination is **required** In the case of ActiveMQ with persistence, you could do this: >>> for i in xrange(1,1000): ... stomp.send({'destination': '/queue/foo', ... 'body': 'Testing', ... 'persistent': 'true'}) """ headers = dict(conf) body = headers.pop("body", "") return self._send_command("SEND", headers, extra={"body": body}, want_receipt=receipt) def _build_frame(self, *args, **kwargs): self._connected_or_raise() return self.frame.build_frame(*args, **kwargs) def subscribe(self, conf=None): """Subscribe to a given destination You will need to pass any headers your STOMP server likes. destination is *required* In the case of ActiveMQ, you could do this: >>> stomp.subscribe({'destination':'/queue/foo', ... 'ack':'client'}) """ destination = conf["destination"] self._send_command("SUBSCRIBE", conf) self._subscribed_to[destination] = True def begin(self, conf=None): """Begin transaction. You will need to pass any headers your STOMP server likes. destination is *required* In the case of ActiveMQ, you could do this: >>> stomp.begin({'transaction':'<randomish_hash_like_thing>'}) """ self._send_command("BEGIN", conf) def commit(self, conf=None): """Commit transaction. You will need to pass any headers your STOMP server likes. destination is **required** In the case of ActiveMQ, you could do this: >>> stomp.commit({'transaction':'<randomish_hash_like_thing>'}) """ self._send_command("COMMIT", conf) def abort(self, conf=None): """Abort transaction. In the case of ActiveMQ, you could do this: >>> stomp.abort({'transaction':'<randomish_hash_like_thing>'}) """ self._send_command("ABORT", conf) def unsubscribe(self, conf=None): """Unsubscribe from a given destination You will need to pass any headers your STOMP server likes. destination is *required* >>> stomp.unsubscribe({'destination':'/queue/foo'}) """ destination = conf["destination"] self._send_command("UNSUBSCRIBE", conf) self._subscribed_to.pop(destination, None) def ack(self, frame): """Acknowledge receipt of a message :param: A :class:`stompy.frame.Frame` instance. Example >>> while True: ... frame = stomp.receive_frame() ... stomp.ack(frame) """ message_id = frame.headers.get('message-id') self._send_command("ACK", {"message-id": message_id}) def receive_frame(self, callback=None, nonblocking=False): """Get a frame from the STOMP server :keyword nonblocking: By default this function waits forever until there is a message to be received, however, in non-blocking mode it returns ``None`` if there is currently no message available. :keyword callback: Optional function to execute when message recieved. Note that you must be subscribed to one or more destinations. Use :meth:`subscribe` to subscribe to a topic/queue. Example: Blocking >>> while True: ... frame = stomp.receive_frame() ... print(frame.headers['message-id']) ... stomp.ack(frame) Example: Non-blocking >>> frame = stomp.recieve_frame(nonblocking=True) >>> if frame: ... process_message(frame) ... else: ... # no messages yet. """ self._connected_or_raise() self._callback = callback message = None if self._callback: message = self.frame.get_message(nb=nonblocking) self._callback(message) return else: return self.frame.get_message(nb=nonblocking) def poll(self, callback=None): """Alias to :meth:`receive_frame` with ``nonblocking=True``.""" return self.receive_frame(nonblocking=True, callback=callback) def send_frame(self, frame): """Send a custom frame to the STOMP server :param frame: A :class:`stompy.frame.Frame` instance. Example >>> from stompy import Frame >>> frame = Frame().build_frame({ ... "command": "DISCONNECT", ... "headers": {}, ... }) >>> stomp.send_frame(frame) """ self._connected_or_raise() frame = self.frame.send_frame(frame.as_string()) return frame def _send_command(self, command, conf=None, extra=None, **kwargs): conf = conf or {} extra = extra or {} frame_conf = {"command": command, "headers": conf} frame_conf.update(extra) frame = self._build_frame(frame_conf, **kwargs) reply = self.send_frame(frame) if kwargs.get("want_receipt", False): return reply return frame def _connected_or_raise(self): if not self.connected: raise self.NotConnectedError("Not connected to STOMP broker.") @property def subscribed(self): """**DEPRECATED** The queue or topic currently subscribed to.""" as_list = self._subscribed_to.keys() if not as_list: return return as_list[0]