Handle disconnects more gracefully in the JS and Python backend.
Move config example to correct location.
Default run script now hides tracebacks.
HMACKEY = "mrmoo"
HMACTEMPORAL = 30
UPDATE_FREQ = 0.5
+MAXBUFSIZE = 10000
this.send = this.connection.send.bind(this.connection);
this.connect = this.connection.connect.bind(this.connection);
- this.disconnect = this.connection.disconnect;
+ this.disconnect = this.connection.disconnect.bind(this.connection);
},
dispatch: function(data) {
var message = data[0];
if(message == "connect") {
this.connected();
} else if(message == "disconnect") {
- this.disconnected();
+ if(data.length == 0) {
+ this.disconnected("No error!");
+ } else {
+ this.disconnected(data[1]);
+ }
+ this.disconnect();
} else if(message == "c") {
var command = data[1].toUpperCase();
}, this);
}, this);
},
- disconnected: function() {
+ disconnected: function(message) {
for(var x in this.parent.channels)
this.ui.closeWindow(x);
this.tracker = undefined;
- this.newServerLine("DISCONNECT");
+ this.newServerLine("DISCONNECT", {"m": message});
},
supported: function(key, value) {
if(key == "PREFIX") {
this.disconnected = false;
},
send: function(data) {
+ if(this.disconnected)
+ return;
var r = new Request.JSON({url: "/e/p/" + this.sessionid + "?c=" + encodeURIComponent(data) + "&t=" + this.counter++, onComplete: function(o) {
- if(o[0] == false)
- alert("An error occured: " + o[1]);
- }});
+ if(!o || (o[0] == false)) {
+ if(!this.disconnected) {
+ this.disconnected = true;
+ alert("An error occured: " + o[1]);
+ }
+ }
+ }.bind(this)});
r.get();
},
- x: function() {
- this.fireEvent("recv", [[false, "moo"]]);
- },
recv: function() {
- if(this.disconnected)
- return;
-
var r = new Request.JSON({url: "/e/s/" + this.sessionid + "?t=" + this.counter++, onComplete: function(o) {
if(o) {
if(o[0] == false) {
- alert("An error occured: " + o[1]);
+ if(!this.disconnected) {
+ this.disconnected = true;
+
+ alert("An error occured: " + o[1]);
+ }
return;
}
o.each(function(x) {
this.fireEvent("recv", [x]);
}, this);
+ } else {
+ if(!this.disconnected) {
+ this.disconnected = true;
+
+ alert("An unknown error occured.");
+ }
+ return;
}
this.recv();
- }.bind(this)});
+ }.bind(this)});
r.get();
},
connect: function() {
"SIGNON": ["Signed on!", true],
"CONNECT": ["Connected to server.", true],
"RAW": ["$m", true],
- "DISCONNECT": ["Disconnected from server.", true],
+ "DISCONNECT": ["Disconnected from server: $m", true],
"ERROR": ["ERROR: $m", true],
"SERVERNOTICE": ["$m", true],
"JOIN": ["$n [$h] has joined $c", true],
from twisted.web import resource, server, static
from twisted.names import client
from twisted.internet import reactor
-import simplejson, md5, sys, os, ircclient, time, config
+import traceback
+import simplejson, md5, sys, os, ircclient, time, config, weakref
Sessions = {}
def get_session_id():
- return md5.md5(os.urandom(16)).hexdigest()
-
+ return md5.md5(os.urandom(16)).hexdigest()[:10]
+
+class BufferOverflowException(Exception):
+ pass
+
def jsondump(fn):
def decorator(*args, **kwargs):
x = fn(*args, **kwargs)
return x
return decorator
+def cleanupSession(id):
+ try:
+ del Sessions[id]
+ except KeyError:
+ pass
+
class IRCSession:
def __init__(self, id):
self.id = id
self.buffer = []
self.throttle = 0
self.schedule = None
-
+ self.closed = False
+ self.cleanupschedule = None
+
def subscribe(self, channel):
self.subscriptions.append(channel)
self.flush()
newsubs.append(x)
self.subscriptions = newsubs
-
+ if self.closed and not self.subscriptions:
+ cleanupSession(self.id)
+
def event(self, data):
+ bufferlen = sum(map(len, self.buffer))
+ if bufferlen + len(data) > config.MAXBUFLEN:
+ self.buffer = []
+ self.client.error("Buffer overflow")
+ return
+
self.buffer.append(data)
self.flush()
def push(self, data):
- self.client.write(data)
-
+ if not self.closed:
+ self.client.write(data)
+
+ def disconnect(self):
+ # keep the session hanging around for a few seconds so the
+ # client has a chance to see what the issue was
+ self.closed = True
+
+ reactor.callLater(5, cleanupSession, self.id)
+
class Channel:
def __init__(self, request):
self.request = request
decoded = command.decode("utf-8")
except UnicodeDecodeError:
decoded = command.decode("iso-8859-1", "ignore")
- session.push(decoded)
+
+ try:
+ session.push(decoded)
+ except AttributeError: # occurs when we haven't noticed an error
+ session.disconnect()
+ return [False, "Connection closed by server."]
+ except Exception, e: # catch all
+ session.disconnect()
+ traceback.print_exc(file=sys.stderr)
+ return [False, "Unknown error."]
+
return [True]
return [False, "404"]
class QWebIRCClient(basic.LineReceiver):
delimiter = "\n"
-
+
def dataReceived(self, data):
basic.LineReceiver.dataReceived(self, data.replace("\r", ""))
def connectionMade(self):
basic.LineReceiver.connectionMade(self)
+ self.lastError = None
f = self.factory.ircinit
nick, ident, ip, realname = f["nick"], f["ident"], f["ip"], f["realname"]
self("connect")
def connectionLost(self, reason):
+ if self.lastError:
+ self.disconnect("Connection to IRC server lost: %s" % self.lastError)
+ else:
+ self.disconnect("Connection to IRC server lost.")
self.factory.client = None
basic.LineReceiver.connectionLost(self, reason)
- self("disconnect")
+
+ def error(self, message):
+ self.lastError = message
+ self.write("QUIT :qwebirc exception: %s" % message)
+ self.transport.loseConnection()
+
+ def disconnect(self, reason):
+ self("disconnect", reason)
+ self.factory.publisher.disconnect()
class QWebIRCFactory(protocol.ClientFactory):
protocol = QWebIRCClient
def write(self, data):
self.client.write(data)
-
+
+ def error(self, reason):
+ self.client.error(reason)
+
+ def clientConnectionFailed(self, reason):
+ protocol.ClientFactory.clientConnectionFailed(reason)
+ self.client.disconnect("Connection to IRC server failed.")
+
def createIRC(*args, **kwargs):
f = QWebIRCFactory(*args, **kwargs)
reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f)
if __name__ == "__main__":
e = createIRC(lambda x: 2, nick="slug__moo", ident="mooslug", ip="1.2.3.6", realname="mooooo")
- reactor.run()
\ No newline at end of file
+ reactor.run()
set PYTHONPATH=.\r
-c:\python25\scripts\twistd.py qwebirc\r
-pause
\ No newline at end of file
+c:\python25\scripts\twistd.py qwebirc -n\r
+pause\r
#!/bin/sh
export PYTHONPATH=.:${PYTHONPATH}
-twistd qwebirc
+twistd qwebirc -n