Simple connection karma to avoid update flood

This commit is contained in:
HelloZeroNet 2016-03-12 23:09:26 +01:00
parent f735862977
commit 555c136143
4 changed files with 31 additions and 5 deletions

View file

@ -86,7 +86,7 @@ class UiRequestPlugin(object):
len(main.file_server.connections), main.file_server.last_connection_id
)
yield "<table><tr> <th>id</th> <th>proto</th> <th>type</th> <th>ip</th> <th>open</th> <th>crypt</th> <th>ping</th>"
yield "<th>buff</th> <th>idle</th> <th>open</th> <th>delay</th> <th>out</th> <th>in</th> <th>last sent</th>"
yield "<th>buff</th> <th>bad</th> <th>idle</th> <th>open</th> <th>delay</th> <th>out</th> <th>in</th> <th>last sent</th>"
yield "<th>waiting</th> <th>version</th> <th>peerid</th> </tr>"
for connection in main.file_server.connections:
if "cipher" in dir(connection.sock):
@ -102,6 +102,7 @@ class UiRequestPlugin(object):
("<span title='%s'>%s</span>", (connection.crypt, cipher)),
("%6.3f", connection.last_ping_delay),
("%s", connection.incomplete_buff_recv),
("%s", connection.bad_actions),
("since", max(connection.last_send_time, connection.last_recv_time)),
("since", connection.start_time),
("%.3f", connection.last_sent_time - connection.last_send_time),

View file

@ -17,7 +17,7 @@ class Connection(object):
"sock", "sock_wrapped", "ip", "port", "cert_pin", "site_lock", "id", "protocol", "type", "server", "unpacker", "req_id",
"handshake", "crypt", "connected", "event_connected", "closed", "start_time", "last_recv_time",
"last_message_time", "last_send_time", "last_sent_time", "incomplete_buff_recv", "bytes_recv", "bytes_sent",
"last_ping_delay", "last_req_time", "last_cmd", "name", "updateName", "waiting_requests", "waiting_streams"
"last_ping_delay", "last_req_time", "last_cmd", "bad_actions", "name", "updateName", "waiting_requests", "waiting_streams"
)
def __init__(self, server, ip, port, sock=None, site_lock=None):
@ -56,6 +56,7 @@ class Connection(object):
self.last_ping_delay = None
self.last_req_time = 0
self.last_cmd = None
self.bad_actions = 0
self.name = None
self.updateName()
@ -75,6 +76,12 @@ class Connection(object):
def log(self, text):
self.server.log.debug("%s > %s" % (self.name, text))
def badAction(self, weight=1):
self.bad_actions += weight
def goodAction(self):
self.bad_actions = 0
# Open connection to peer and wait for handshake
def connect(self):
self.log("Connecting...")

View file

@ -164,7 +164,9 @@ class ConnectionServer:
self.connections.remove(connection)
def checkConnections(self):
run_i = 0
while self.running:
run_i += 1
time.sleep(60) # Check every minute
self.ip_incoming = {} # Reset connected ips counter
self.broken_ssl_peer_ids = {} # Reset broken ssl peerids count
@ -205,3 +207,11 @@ class ConnectionServer:
elif idle > 60 and connection.protocol == "?": # No connection after 1 min
connection.log("[Cleanup] Connect timeout: %s" % idle)
connection.close()
elif idle < 60 and connection.bad_actions > 40:
connection.log("[Cleanup] Too many bad actions: %s" % connection.bad_actions)
connection.close()
elif run_i % 30 == 0:
# Reset bad action counter every 30 min
connection.bad_actions = 0

View file

@ -57,14 +57,16 @@ class FileRequest(object):
if "site" in params and self.connection.site_lock and self.connection.site_lock not in (params["site"], "global"):
self.response({"error": "Invalid site"})
self.log.error("Site lock violation: %s != %s" % (self.connection.site_lock != params["site"]))
self.connection.badAction(5)
return False
if cmd == "update":
event = "%s update %s %s" % (self.connection.id, params["site"], params["inner_path"])
if not RateLimit.isAllowed(event): # There was already an update for this file in the last 10 second
time.sleep(5)
self.response({"ok": "File update queued"})
# If called more than once within 10 sec only keep the last update
RateLimit.callAsync(event, 10, self.actionUpdate, params)
RateLimit.callAsync(event, max(self.connection.bad_actions, 10), self.actionUpdate, params)
else:
func_name = "action" + cmd[0].upper() + cmd[1:]
func = getattr(self, func_name, None)
@ -81,8 +83,8 @@ class FileRequest(object):
return False
if site.settings["own"] and params["inner_path"].endswith("content.json"):
self.log.debug(
"Someone trying to push a file to own site %s, reload local %s first" %
(site.address, params["inner_path"])
"%s pushing a file to own site %s, reloading local %s first" %
(self.connection.ip, site.address, params["inner_path"])
)
changed, deleted = site.content_manager.loadContent(params["inner_path"], add_bad_files=False)
if changed or deleted: # Content.json changed locally
@ -107,6 +109,7 @@ class FileRequest(object):
)
self.response({"ok": "Thanks, file %s updated!" % params["inner_path"]})
self.connection.goodAction()
elif valid is None: # Not changed
if params.get("peer"):
@ -125,10 +128,12 @@ class FileRequest(object):
site.needFile(task["inner_path"], peer=peer, update=True, blocking=False)
self.response({"ok": "File not changed"})
self.connection.badAction()
else: # Invalid sign or sha1 hash
self.log.debug("Update for %s is invalid" % params["inner_path"])
self.response({"error": "File invalid"})
self.connection.badAction(5)
# Send file content request
def actionGetFile(self, params):
@ -302,6 +307,7 @@ class FileRequest(object):
site = self.sites.get(params["site"])
if not site or not site.settings["serving"]: # Site unknown or not serving
self.response({"error": "Unknown site"})
self.connection.badAction(5)
return False
found = site.worker_manager.findOptionalHashIds(params["hash_ids"])
@ -340,6 +346,7 @@ class FileRequest(object):
site = self.sites.get(params["site"])
if not site or not site.settings["serving"]: # Site unknown or not serving
self.response({"error": "Unknown site"})
self.connection.badAction(5)
return False
peer = site.addPeer(self.connection.ip, self.connection.port, return_peer=True, connection=self.connection) # Add or get peer
@ -375,3 +382,4 @@ class FileRequest(object):
# Unknown command
def actionUnknown(self, cmd, params):
self.response({"error": "Unknown command: %s" % cmd})
self.connection.badAction(5)