Dawid Dave Kosiński
Dawid Dave Kosiński

Reputation: 901

How to start app using sockets Tornado 4.4

I'm a newbie to creating manually sockets. My OS is ubuntu. I've got an proxy server written python using Tornado, everything is fine when I use the "fast version" starting the app, I mean the:

if __name__ == "__main__":
    app = make_app()
    port = options.port # default 8000
    if len(sys.argv) > 1:
        port = int(sys.argv[1])
    app.listen(port)
    print 'tornado working on port %s' % port
    tornado.ioloop.IOLoop.current().start()

But when i want to change it to use the 'socket version' it seems that I'm doing something wrong. I get an error saying that the address is already used.

code:

def make_app():
    return MyApplication()

def connection_ready(sock, fd, events):
    while True:
        try:
            connection, address = sock.accept()
        except socket.error as e:
            if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN):
                raise
            return
        connection.setblocking(0)
        app = make_app()
        app.listen(8000) # I get here an error: [Errno 98] Address already in use

if __name__ == "__main__":
    port = options.port # default port 8000
    if len(sys.argv) > 1:
        port = int(sys.argv[1])
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.setblocking(False)
    sock.bind(("", port))
    sock.listen(128)
    io_loop = tornado.ioloop.IOLoop.current()
    callback = functools.partial(connection_ready, sock)
    io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
    io_loop.start()

I'm trying to implement the same way that the documentation says (http://www.tornadoweb.org/en/stable/ioloop.html) but I don't see it starting the app in there.

Could someone tell me what is the proper way to start an app using sockets? I'm trying to accomplish an application that is available when the sever accepts the incoming socket. (So every client that connects to my listining port described in the main function at lines:sock.bind(("", port)) and sock.listen(128) will get a new socket and have access to the application).

Edit: I'm adding my proxy class:

class ProxyHandler(tornado.web.RequestHandler):

    SUPPORTED_METHODS = ['GET', 'POST']

    def data_received(self, chunk):
        pass

    def compute_etag(self):
        return None  # disable tornado Etag

    def handle_response(self, response):
        if response.error and not isinstance(response.error, tornado.httpclient.HTTPError):
            self.set_status(500)
            self.write('Internal server error:\n' + str(response.error))
        else:
            self.set_status(response.code, response.reason)
            self._headers = tornado.httputil.HTTPHeaders()  # clear tornado default header

            for header, v in response.headers.get_all():
                 if header not in ('Content-Length', 'Transfer-Encoding', 'Content-Encoding', 'Connection'):
                    self.add_header(header, v)  # some header appear multiple times, eg 'Set-Cookie'

            secured_page = False
            for page in secure_pages:
                if page in self.request.uri:
                    secured_page = True
                    self.set_header('Content-Length', len(response.body))
                    self.write(response.body)
                    break
            if response.body and not secured_page:
                c.execute('SELECT filter_name FROM filters WHERE filter_type=1')
                tags = c.fetchall()
                soup = BeautifulSoup(response.body, 'html.parser')
                for row in tags:
                    catched_tags = soup.find_all(str(row[0]))
                    if catched_tags:
                        print 'catched: %s of <%s> tags' % (len(catched_tags), str(row[0]))
                    for tag in catched_tags:
                        tag.extract()
                new_body = str(soup)
                self.set_header('Content-Length', len(new_body))
                self.write(new_body)
        self.finish()

    @tornado.web.asynchronous
    def get(self):
        logger.debug('Handle %s request to %s', self.request.method, self.request.uri)

        body = self.request.body
        if not body:
            body = None

        try:
            if 'Proxy-Connection' in self.request.headers:
                del self.request.headers['Proxy-Connection']

            c.execute('SELECT filter_name FROM filters WHERE filter_type=2')
            urls = c.fetchall()
            for url in urls:
                if url[0] in self.request.path:
                    self.set_status(403)
                    self.finish()
                    return

            fetch_request(self.request.uri, self.handle_response,
                      method=self.request.method, body=body, headers=self.request.headers, follow_redirects=False,
                      allow_nonstandard_methods=True)
        except tornado.httpclient.HTTPError as e:
            if hasattr(e, 'response') and e.response:
                self.handle_response(e.response)
            else:
                self.set_status(500)
                self.write('Internal server error:\n' + str(e))
                self.finish()

    @tornado.web.asynchronous
    def post(self):
        return self.get()

And my urls for the application:

urls = [
url(r"/admin/$", mainHandlers.MainHandler),
url(r"/admin/delete_filter/", mainHandlers.DataDeleteHandler),
url(r"/admin/filters/$", mainHandlers.DataGetter),
url(r"/admin/new_filter/$", mainHandlers.FormHandler),
url(r"/admin/stats/$", mainHandlers.StatsTableHandler),
url(r"/admin/stats/query/$", mainHandlers.AjaxStatsGetHandler),
url(r"/static/", StaticFileHandler, dict(path=settings['static_path'])),
url(r'.*', myProxy.ProxyHandler),
]

Upvotes: 0

Views: 136

Answers (3)

Dawid Dave Kosiński
Dawid Dave Kosiński

Reputation: 901

I've rewitten my Proxy to pure Python code on sockets, I'm not using URL's now and I only handle the responses from the remote addresses. I'm not using any framework

Upvotes: 0

Ben Darnell
Ben Darnell

Reputation: 22134

It says the port is already in use because it is. You're listening on port 8000 at least twice: once in the __main__ block when you call sock.listen, and again in the connection_ready handler when you call app.listen() (which creates another socket and tries to bind it to port 8000). You need to remove the app.listen() line, but I don't understand what you're trying to do well enough to say what you should do instead.

Upvotes: 1

Marcin Świerczyna
Marcin Świerczyna

Reputation: 112

If you start app on Windows, you must wait for the firewall unblock. In windows it is safe to assume that if an application occupies a port it is blocked for use by other processes that might listen to packets not intended for them.

Upvotes: 0

Related Questions