1# -*- coding: utf-8 -*-
2"""
3    sockjs.tornado.transports.rawwebsocket
4    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6    Raw websocket transport implementation
7"""
8import logging
9import socket
10
11from sockjs.tornado import websocket, session
12from sockjs.tornado.transports import base
13
14LOG = logging.getLogger("tornado.general")
15
16class RawSession(session.BaseSession):
17    """Raw session without any sockjs protocol encoding/decoding. Simply
18    works as a proxy between `SockJSConnection` class and `RawWebSocketTransport`."""
19    def send_message(self, msg, stats=True, binary=False):
20        self.handler.send_pack(msg, binary)
21
22    def on_message(self, msg):
23        self.conn.on_message(msg)
24
25
26class RawWebSocketTransport(websocket.SockJSWebSocketHandler, base.BaseTransportMixin):
27    """Raw Websocket transport"""
28    name = 'rawwebsocket'
29
30    def initialize(self, server):
31        self.server = server
32        self.session = None
33        self.active = True
34
35    def open(self):
36        # Stats
37        self.server.stats.on_conn_opened()
38
39        # Disable nagle if needed
40        if self.server.settings['disable_nagle']:
41            self.stream.socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
42
43        # Create and attach to session
44        self.session = RawSession(self.server.get_connection_class(), self.server)
45        self.session.set_handler(self)
46        self.session.verify_state()
47
48    def _detach(self):
49        if self.session is not None:
50            self.session.remove_handler(self)
51            self.session = None
52
53    def on_message(self, message):
54        # SockJS requires that empty messages should be ignored
55        if not message or not self.session:
56            return
57
58        try:
59            self.session.on_message(message)
60        except Exception:
61            LOG.exception('RawWebSocket')
62
63            # Close running connection
64            self.abort_connection()
65
66    def on_close(self):
67        # Close session if websocket connection was closed
68        if self.session is not None:
69            # Stats
70            self.server.stats.on_conn_closed()
71
72            session = self.session
73            self._detach()
74            session.close()
75
76    def send_pack(self, message, binary=False):
77        # Send message
78        try:
79            self.write_message(message, binary)
80        except IOError:
81            self.server.io_loop.add_callback(self.on_close)
82
83    def session_closed(self):
84        try:
85            self.close()
86        except IOError:
87            pass
88        finally:
89            self._detach()
90
91    # Websocket overrides
92    def allow_draft76(self):
93        return True
94