# Upgrade header should be present and should be equal to WebSocket if self.request.headers.get("Upgrade", "").lower() != 'websocket': self.set_status(400) log_msg = "Can \"Upgrade\" only to \"WebSocket\"." self.finish(log_msg) gen_log.debug(log_msg) return
# Connection header should be upgrade. # Some proxy servers/load balancers # might mess with it. headers = self.request.headers connection = map(lambda s: s.strip().lower(), headers.get("Connection", "").split(",")) if'upgrade' not in connection: self.set_status(400) log_msg = "\"Connection\" must be \"Upgrade\"." self.finish(log_msg) gen_log.debug(log_msg) return
# Handle WebSocket Origin naming convention differences # The difference between version 8 and 13 is that in 8 the # client sends a "Sec-Websocket-Origin" header and in 13 it's # simply "Origin". if"Origin"in self.request.headers: origin = self.request.headers.get("Origin") else: origin = self.request.headers.get("Sec-Websocket-Origin", None)
# If there was an origin header, check to make sure it matches # according to check_origin. When the origin is None, we assume it # did not come from a browser and that it can be passed on. if origin is not None and not self.check_origin(origin): self.set_status(403) log_msg = "Cross origin websockets not allowed" self.finish(log_msg) gen_log.debug(log_msg) return
@property def ping_interval(self): """The interval for websocket keep-alive pings. Set websocket_ping_interval = 0 to disable pings. """ return self.settings.get('websocket_ping_interval', None)
@property def ping_timeout(self): """If no ping is received in this many seconds, close the websocket connection (VPNs, etc. can fail to cleanly close ws connections). Default is max of 3 pings or 30 seconds. """ return self.settings.get('websocket_ping_timeout', None) def ping(self, data): """Send ping frame to the remote end.""" if self.ws_connection is None: raise WebSocketClosedError() self.ws_connection.write_ping(data)
def on_pong(self, data): """Invoked when the response to a ping frame is received.""" pass
def on_ping(self, data): """Invoked when the a ping frame is received.""" pass
@property def max_message_size(self): """Maximum allowed message size. If the remote peer sends a message larger than this, the connection will be closed. Default is 10MiB. """ return self.settings.get('websocket_max_message_size', None)
def write_message(self, message, binary=False): """Sends the given message to the client of this Web Socket. The message may be either a string or a dict (which will be encoded as json). If the ``binary`` argument is false, the message will be sent as utf8; in binary mode any byte string is allowed. If the connection is already closed, raises `WebSocketClosedError`. .. versionchanged:: 3.2 `WebSocketClosedError` was added (previously a closed connection would raise an `AttributeError`) .. versionchanged:: 4.3 Returns a `.Future` which can be used for flow control. """ if self.ws_connection is None: raise WebSocketClosedError() if isinstance(message, dict): message = tornado.escape.json_encode(message) return self.ws_connection.write_message(message, binary=binary)
def check_origin(self, origin): """Override to enable support for allowing alternate origins. The ``origin`` argument is the value of the ``Origin`` HTTP header, the url responsible for initiating this request. This method is not called for clients that do not send this header; such requests are always allowed (because all browsers that implement WebSockets support this header, and non-browser clients do not have the same cross-site security concerns). Should return True to accept the request or False to reject it. By default, rejects all requests with an origin on a host other than this one. This is a security protection against cross site scripting attacks on browsers, since WebSockets are allowed to bypass the usual same-origin policies and don't use CORS headers. .. warning:: This is an important security measure; don't disable it without understanding the security implications. In particular, if your authentication is cookie-based, you must either restrict the origins allowed by ``check_origin()`` or implement your own XSRF-like protection for websocket connections. See `these <https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html>`_ `articles <https://devcenter.heroku.com/articles/websocket-security>`_ for more. To accept all cross-origin traffic (which was the default prior to Tornado 4.0), simply override this method to always return true:: def check_origin(self, origin): return True To allow connections from any subdomain of your site, you might do something like:: def check_origin(self, origin): parsed_origin = urllib.parse.urlparse(origin) return parsed_origin.netloc.endswith(".mydomain.com") .. versionadded:: 4.0 """ parsed_origin = urlparse(origin) origin = parsed_origin.netloc origin = origin.lower()
host = self.request.headers.get("Host")
# Check to see that origin matches host directly, including ports return origin == host
重写这个方法 return true则允许跨域请求
建立连接之后
1 2 3 4 5 6 7 8 9
def open(self, *args, **kwargs): """Invoked when a new WebSocket is opened. The arguments to `open` are extracted from the `tornado.web.URLSpec` regular expression, just like the arguments to `tornado.web.RequestHandler.get`. """ pass
可重写open方法,在建立连接之后进行业务处理
接收消息
1 2 3 4 5 6 7 8 9 10
def on_message(self, message): """Handle incoming messages on the WebSocket This method must be overridden. .. versionchanged:: 4.5 ``on_message`` can be a coroutine. """ raise NotImplementedError
def on_close(self): """Invoked when the WebSocket is closed. If the connection was closed cleanly and a status code or reason phrase was supplied, these values will be available as the attributes ``self.close_code`` and ``self.close_reason``. .. versionchanged:: 4.0 Added ``close_code`` and ``close_reason`` attributes. """ pass
def close(self, code=None, reason=None): """Closes this Web Socket. Once the close handshake is successful the socket will be closed. ``code`` may be a numeric status code, taken from the values defined in `RFC 6455 section 7.4.1 <https://tools.ietf.org/html/rfc6455#section-7.4.1>`_. ``reason`` may be a textual message about why the connection is closing. These values are made available to the client, but are not otherwise interpreted by the websocket protocol. .. versionchanged:: 4.0 Added the ``code`` and ``reason`` arguments. """ if self.ws_connection: self.ws_connection.close(code, reason) self.ws_connection = None
def send_error(self, *args, **kwargs): if self.stream is None: super(WebSocketHandler, self).send_error(*args, **kwargs) else: # If we get an uncaught exception during the handshake, # we have no choice but to abruptly close the connection. # TODO: for uncaught exceptions after the handshake, # we can close the connection more gracefully. self.stream.close()