Serwer Tornado zgłasza błąd Strumień jest zamknięty

Próbuję zaimplementować bardzo prostą stronę internetową do czatu / echa.

Gdy klient odwiedza / powiadamia: 8000 zwykła strona ładuje się, po stronie klienta inicjowane jest żądanie ustanowienia odbiorcy, na zapleczu liczba chmur jest aktualizowana i wysyłana do wszystkich istniejących klientów.

Za każdym razem, gdy użytkownik wprowadza coś w polu tekstowym, test POST jest wykonywany na zapleczu, a wszyscy inni klienci otrzymują aktualizację z tym tekstem.

Oto szablon frontonu

<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
</head>
<body>
    <p>session: <span id="session">{{ session }}</span><br/>
    client count: <span id="clientCount">{{ clientCount }}</span><br/>
    <span id="welcome">...registering with notify...</span></p>

    <div style="width: 600px; height: 100px; border: 1px solid black; overflow:auto; padding: 4px;" id="chatText">

    </div>

    <div>
    <span>enter text below:</span><br />
    <input type="text" id="textInput"></textarea>
    <div>

<script>

$(document).ready(function() {
    document.session = $('#session').html();
    setTimeout(initializeListener, 100);


    $('#textInput').keypress(function(e) {
    //    e.preventDefault(); 
        if(e.keyCode == 13 && !e.shiftKey) {
            var text = $('#textInput').val();
            submitChatText(text);
            return false;
        }
    });
});

var logon = '1';

function initializeListener() {
    console.log('initializeListener() called');
    jQuery.getJSON('//localhost/notify/listen', {logon: logon, session: document.session},
        function(data, status, xhr) {
            console.log('initializeListener() returned');
            if ('clientCount' in data) {
                $('#clientCount').html(data['clientCount']);
            }
            if ('chatText' in data) {
                text = $('#chatText').html()
                $('#chatText').html(data['chatText'] + "<br />\n" + text);
            }
            if (logon == '1') {
                $('#welcome').html('registered listener with notify!');
                logon = '0';
            }
            setTimeout(initializeListener, 0);
        })
        .error(function(XMLHttpRequest, textStatus, errorThrown) {
            console.log('error: '+textStatus+' ('+errorThrown+')');
            setTimeout(initializeListener, 100);
        });
}

function submitChatText(text) {
    console.log('submitChatText called with text: '+text)
    jQuery.ajax({
        url: '//localhost/notify/send',
        type: 'POST',
        data: {
            session: document.session,
            text: ''+text
        },
        dataType: 'json',
        //beforeSend: function(xhr, settings) {
        //    $(event.target).attr('disabled', 'disabled');
        //},
        success: function(data, status, xhr) {
            console.log('sent text message')
            $("#textInput").val('');
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            console.log('error: '+textStatus+' ('+errorThrown+')');
        }
    });


}

</script>
</body>
</html>

Oto kod serwera:

import tornado.ioloop
import tornado.web
import tornado.options
from uuid import uuid4
import json

class Client(object):
    callbacks = {}
    chat_text = ''

    def register(self, callback, session, logon=False):
        self.callbacks[session] = callback
        if logon == '1':
            self.notifyCallbacks()

    def notifyCallbacks(self):
        result = {}
        result['clientCount'] = self.getClientCount()
        if self.chat_text:
            result['chatText'] = self.chat_text

        for session, callback in self.callbacks.iteritems():
            callback(result)

        self.callbacks = {}

    def sendText(self, session, text):
        self.chat_text = text
        self.notifyCallbacks()
        self.chat_text = ''

    def getClientCount(self):
        return len(self.callbacks)

class Application(tornado.web.Application):
    def __init__(self):
        self.client = Client()
        handlers = [
            (r"/notify", MainHandler),
            (r"/notify/listen", ListenHandler),
            (r"/notify/send", SendHandler)
        ]
        settings = dict(
            cookie_secret="43oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
            template_path="templates/notify",
        )
        tornado.web.Application.__init__(self, handlers, **settings)

class ListenHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        logon = self.get_argument('logon')
        session = self.get_argument('session')
        self.application.client.register(self.on_message, session, logon)

    def on_message(self, result):
        json_result = json.dumps(result)
        self.write(json_result)
        self.finish()

class SendHandler(tornado.web.RequestHandler):
    def post(self):
        text = self.get_argument('text')
        session = self.get_argument('session')
        self.application.client.sendText(session, text)

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        session = uuid4()
        client_count= self.application.client.getClientCount()
        self.render("testpage.html", session=session, clientCount=client_count)


if __name__ == '__main__':
    application = Application()
    application.listen(8000)
    tornado.ioloop.IOLoop.instance().start()

Czasami serwer zgłasza błędy, gdy zamykam jedną kartę i próbuję nadawać z innej. Błąd jest następujący:

    ERROR:root:Uncaught exception POST /notify/send (127.0.0.1)
HTTPRequest(protocol='http', host='localhost', method='POST', uri='/notify/send', version='HTTP/1.1', remote_ip='127.0.0.1', body='session=e5608630-e2c7-4e1a-baa7-0d74bc0ec9fc&text=swff', headers={'Origin': 'http://localhost', 'Content-Length': '54', 'Accept-Language': 'en-US,en;q=0.8', 'Accept-Encoding': 'gzip,deflate,sdch', 'X-Forwarded-For': '127.0.0.1', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5', 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', 'Host': 'localhost', 'X-Requested-With': 'XMLHttpRequest', 'X-Real-Ip': '127.0.0.1', 'Referer': 'http://localhost/notify', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'})
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/tornado/web.py", line 1021, in _execute
    getattr(self, self.request.method.lower())(*args, **kwargs)
  File "test.py", line 66, in post
    self.application.client.sendText(session, text)
  File "test.py", line 30, in sendText
    self.notifyCallbacks()
  File "test.py", line 24, in notifyCallbacks
    callback(result)
  File "test.py", line 60, in on_message
    self.finish()
  File "/usr/lib/python2.7/site-packages/tornado/web.py", line 701, in finish
    self.request.finish()
  File "/usr/lib/python2.7/site-packages/tornado/httpserver.py", line 433, in finish
    self.connection.finish()
  File "/usr/lib/python2.7/site-packages/tornado/httpserver.py", line 187, in finish
    self._finish_request()
  File "/usr/lib/python2.7/site-packages/tornado/httpserver.py", line 223, in _finish_request
    self.stream.read_until(b("\r\n\r\n"), self._header_callback)
  File "/usr/lib/python2.7/site-packages/tornado/iostream.py", line 153, in read_until
    self._try_inline_read()
  File "/usr/lib/python2.7/site-packages/tornado/iostream.py", line 381, in _try_inline_read
    self._check_closed()
  File "/usr/lib/python2.7/site-packages/tornado/iostream.py", line 564, in _check_closed
    raise IOError("Stream is closed")
IOError: Stream is closed

Oczywiście jest tak dlatego, że karta została zamknięta, więc klient nie słucha już dłużej, ale powinno to być normalną rzeczą, której można się spodziewać, jak mogę sobie z tym poradzić?

Jedyne, co mogłem znaleźć o tym błędzie, to kolejny post stackoverflow, sugerowałem sprawdzenie, czy połączenie zostało zakończone przed wywołaniem metody finish ():

if not self._finished:
    self.finish()

Jednak kiedy to spróbowałem, nie wydawało mi się, że nadal mam ten sam błąd, LUB że dostanę inny błądAssertionError: Żądanie zamknięte w której nie mogłem znaleźć żadnej pomocy.

questionAnswers(1)

yourAnswerToTheQuestion