Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Netutil base protocol implementation #402

Closed
wants to merge 5 commits into from

3 participants

@kachayev

If we will use netutil.TCPServer for implementing some functionality, we will have to use inheritance and write duplicated code more and more times... (example of such application you can find here https://gist.github.com/1387470).

The keynote here that there are many situation when we don't want to create server class (inherited from netutil.TCPServer), we just want to use custom handler as wrapper for incoming IOStream. We can easily avoid this problem, code example in docstring to BaseProtocol. Duplicate this example here:

class EchoProtocol(BaseProtocol):
    def _on_connect(self):
        self._wait()

    def _wait(self):
        self.stream.read_unit(b('\n'), self._on_read)

    def _on_read(self, line):
        self.stream.write(line, callback=self._wait)

server = TCPServer(protocol=EchoProtocol)
server.listen(8888)
IOLoop.instance().start() 

It also possible to implement this approach for httpserver.HTTPConnection (request_callback, no_keep_alive and xheaders can be read from server param). But I don't know if it will bring many benefits (only one - standard approach).

tornado/netutil.py
@@ -217,6 +225,50 @@ class TCPServer(object):
logging.error("Error in connection callback", exc_info=True)
+class BaseProtocol(object):
+ """Base class for implementing handlers for TCP connections.
+
+ Example of how to use ``BaseProtocol`` for impelementing simple

it should be: "... to implement a simple"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@bdarnell
Owner

Closing out this PR; subclassing is the way TCPServer works.

@bdarnell bdarnell closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 55 additions and 3 deletions.
  1. +55 −3 tornado/netutil.py
View
58 tornado/netutil.py
@@ -82,11 +82,13 @@ class TCPServer(object):
your listening sockets in some way other than
`bind_sockets`.
"""
- def __init__(self, io_loop=None, ssl_options=None):
+ def __init__(self, io_loop=None, ssl_options=None, protocol=None):
self.io_loop = io_loop
self.ssl_options = ssl_options
self._sockets = {} # fd -> socket object
self._pending_sockets = []
+ # Class or callable object for handing IOStream
+ self._protocol = protocol
self._started = False
def listen(self, port, address=""):
@@ -186,8 +188,14 @@ def stop(self):
sock.close()
def handle_stream(self, stream, address):
- """Override to handle a new `IOStream` from an incoming connection."""
- raise NotImplementedError()
+ """Create object of stream handler if ``protocol`` was passed.
+
+ If you didn't provide ``protocol`` param, you should override
+ this method to handle a new `IOStream` from an incoming connection."""
+ if self._protocol:
+ self._protocol(stream, address, server=self)
+ else:
+ raise NotImplementedError('Set protocol or override this method')
def _handle_connection(self, connection, address):
if self.ssl_options is not None:
@@ -217,6 +225,50 @@ def _handle_connection(self, connection, address):
logging.error("Error in connection callback", exc_info=True)
+class BaseProtocol(object):
+ """Base class for implementing handlers for TCP connections.
+
+ Example of how to use ``BaseProtocol`` to implement a simple
+ Echo server (just return to client each received line):
+
+ class EchoProtocol(BaseProtocol):
+ def _on_connect(self):
+ self._wait()
+
+ def _wait(self):
+ self.stream.read_unit(b('\n'), self._on_read)
+
+ def _on_read(self, line):
+ self.stream.write(line, callback=self._wait)
+
+ server = TCPServer(protocol=EchoProtocol)
+ server.listen(8888)
+ IOLoop.instance().start()
+ """
+ def __init__(self, stream, address, server=None):
+ self.stream = stream
+ if self.stream.socket.family not in (socket.AF_INET, socket.AF_INET6):
+ # Unix (or other) socket; fake the remote address
+ address = ('0.0.0.0', 0)
+ self.address = address
+ self.server = server
+
+ # Set _on_disconnect method as callback for stream closing
+ self.stream.set_close_callback(self._on_disconnect)
+
+ # Call special method which you can override to
+ # provide custom logic without recalling __init__
+ # from inherited class
+ self._on_connect()
+
+ def _on_connect(self):
+ """Override to provide some actions after connection is established"""
+ pass
+
+ def _on_disconnect(self):
+ """Override to provide some actions after stream is closed"""
+ pass
+
def bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=128):
"""Creates listening sockets bound to the given port and address.
Something went wrong with that request. Please try again.