rev125, Class statistics, OpenSSL disabled on OSX by default because of possible segfault, --disable_openssl command line parameter, Save memory on Connection, Peer and FileRequest objects using slots, Dont store modification time from the far future, Able to query modified files from peer, Allow reannounce in 30secs, Use with command in SiteStorage, Always create dir before write file, PeerCmd shell command to query specific command from peer

This commit is contained in:
HelloZeroNet 2015-04-29 23:12:45 +02:00
parent 71be41ade0
commit 099fe575a0
14 changed files with 126 additions and 23 deletions

View file

@ -119,7 +119,7 @@ class UiRequestPlugin(object):
yield "</table>" yield "</table>"
# Objects # Object types
obj_count = {} obj_count = {}
for obj in gc.get_objects(): for obj in gc.get_objects():
@ -135,6 +135,24 @@ class UiRequestPlugin(object):
yield " - %.1fkb = %s x <a href=\"/Listobj?type=%s\">%s</a><br>" % (stat[1], stat[0], obj, cgi.escape(obj)) yield " - %.1fkb = %s x <a href=\"/Listobj?type=%s\">%s</a><br>" % (stat[1], stat[0], obj, cgi.escape(obj))
# Classes
class_count = {}
for obj in gc.get_objects():
obj_type = str(type(obj))
if obj_type != "<type 'instance'>": continue
class_name = obj.__class__.__name__
if not class_name in class_count:
class_count[class_name] = [0, 0]
class_count[class_name][0] += 1 # Count
class_count[class_name][1] += float(sys.getsizeof(obj))/1024 # Size
yield "<br><br><b>Classes in memory (types: %s, total: %s, %.2fkb):</b><br>" % (len(class_count), sum([stat[0] for stat in class_count.values()]), sum([stat[1] for stat in class_count.values()]))
for obj, stat in sorted(class_count.items(), key=lambda x: x[1][0], reverse=True): # Sorted by count
yield " - %.1fkb = %s x <a href=\"/Dumpobj?class=%s\">%s</a><br>" % (stat[1], stat[0], obj, cgi.escape(obj))
from greenlet import greenlet from greenlet import greenlet
objs = [obj for obj in gc.get_objects() if isinstance(obj, greenlet)] objs = [obj for obj in gc.get_objects() if isinstance(obj, greenlet)]
yield "<br>Greenlets (%s):<br>" % len(objs) yield "<br>Greenlets (%s):<br>" % len(objs)
@ -203,6 +221,29 @@ class UiRequestPlugin(object):
yield "Done in %.1f" % (time.time()-s) yield "Done in %.1f" % (time.time()-s)
def actionDumpobj(self):
import gc, sys
self.sendHeader()
class_filter = self.get.get("class")
yield """
<style>
* { font-family: monospace; white-space: pre }
table * { text-align: right; padding: 0px 10px }
</style>
"""
objs = gc.get_objects()
for obj in objs:
obj_type = str(type(obj))
if obj_type != "<type 'instance'>" or obj.__class__.__name__ != class_filter: continue
yield "%.1fkb %s... " % (float(sys.getsizeof(obj))/1024, cgi.escape(str(obj)) )
for attr in dir(obj):
yield "- %s: %s<br>" % (attr, cgi.escape(str(getattr(obj, attr))))
yield "<br>"
def actionListobj(self): def actionListobj(self):
import gc, sys import gc, sys

View file

@ -4,7 +4,7 @@ import ConfigParser
class Config(object): class Config(object):
def __init__(self): def __init__(self):
self.version = "0.2.9" self.version = "0.2.9"
self.rev = 122 self.rev = 125
self.parser = self.createArguments() self.parser = self.createArguments()
argv = sys.argv[:] # Copy command line arguments argv = sys.argv[:] # Copy command line arguments
argv = self.parseConfig(argv) # Add arguments from config file argv = self.parseConfig(argv) # Add arguments from config file
@ -16,6 +16,11 @@ class Config(object):
return str(self.arguments).replace("Namespace", "Config") # Using argparse str output return str(self.arguments).replace("Namespace", "Config") # Using argparse str output
# Convert string to bool
def strToBool(self, v):
return v.lower() in ("yes", "true", "t", "1")
# Create command line arguments # Create command line arguments
def createArguments(self): def createArguments(self):
# Platform specific # Platform specific
@ -23,9 +28,14 @@ class Config(object):
coffeescript = "type %s | tools\\coffee\\coffee.cmd" coffeescript = "type %s | tools\\coffee\\coffee.cmd"
else: else:
coffeescript = None coffeescript = None
if sys.platform.startswith("Darwin"): # For some reasons openssl doesnt works on mac yet (https://github.com/HelloZeroNet/ZeroNet/issues/94)
disable_openssl = True
else:
disable_openssl = False
# Create parser # Create parser
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.register('type','bool', self.strToBool)
subparsers = parser.add_subparsers(title="Action to perform", dest="action") subparsers = parser.add_subparsers(title="Action to perform", dest="action")
# Main # Main
@ -72,6 +82,13 @@ class Config(object):
action.add_argument('site', help='Site address') action.add_argument('site', help='Site address')
action.add_argument('filename', help='File name to request') action.add_argument('filename', help='File name to request')
# PeerGetFile
action = subparsers.add_parser("peerCmd", help='Request and print a file content from peer')
action.add_argument('peer_ip', help='Peer ip')
action.add_argument('peer_port', help='Peer port')
action.add_argument('cmd', help='Command to execute')
action.add_argument('parameters', help='Parameters to command', nargs='?')
# Config parameters # Config parameters
@ -88,8 +105,9 @@ class Config(object):
parser.add_argument('--fileserver_ip', help='FileServer bind address', default="*", metavar='ip') parser.add_argument('--fileserver_ip', help='FileServer bind address', default="*", metavar='ip')
parser.add_argument('--fileserver_port',help='FileServer bind port', default=15441, type=int, metavar='port') parser.add_argument('--fileserver_port',help='FileServer bind port', default=15441, type=int, metavar='port')
parser.add_argument('--disable_zeromq', help='Disable compatibility with old clients', action='store_true') parser.add_argument('--disable_zeromq', help='Disable compatibility with old clients', action='store_true')
parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port') parser.add_argument('--disable_openssl',help='Disable usage of OpenSSL liblary', type='bool', choices=[True, False], default=disable_openssl)
parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true') parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true')
parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port')
parser.add_argument('--ip_external', help='External ip (tested on start if None)', metavar='ip') parser.add_argument('--ip_external', help='External ip (tested on start if None)', metavar='ip')
parser.add_argument('--coffeescript_compiler', help='Coffeescript compiler for developing', default=coffeescript, metavar='executable_path') parser.add_argument('--coffeescript_compiler', help='Coffeescript compiler for developing', default=coffeescript, metavar='executable_path')

View file

@ -12,7 +12,9 @@ if not config.disable_zeromq:
class Connection: class Connection(object):
__slots__ = ("sock", "ip", "port", "peer_id", "id", "protocol", "type", "server", "unpacker", "req_id", "handshake", "connected", "event_connected", "closed", "zmq_sock", "zmq_queue", "zmq_working", "forward_thread", "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")
def __init__(self, server, ip, port, sock=None): def __init__(self, server, ip, port, sock=None):
self.sock = sock self.sock = sock
self.ip = ip self.ip = ip
@ -315,7 +317,7 @@ class Connection:
self.sock.shutdown(gevent.socket.SHUT_WR) self.sock.shutdown(gevent.socket.SHUT_WR)
self.sock.close() self.sock.close()
except Exception, err: except Exception, err:
if config.debug_socket: self.log("Close error: %s" % Debug.formatException(err)) if config.debug_socket: self.log("Close error: %s" % err)
# Little cleanup # Little cleanup
del self.unpacker del self.unpacker

View file

@ -70,7 +70,7 @@ class ContentManager:
self.site.bad_files[inner_path] = True self.site.bad_files[inner_path] = True
if new_content["modified"] > self.site.settings.get("modified", 0): if new_content["modified"] > self.site.settings.get("modified", 0):
self.site.settings["modified"] = new_content["modified"] self.site.settings["modified"] = min(time.time()+60*10, new_content["modified"]) # Dont store modifications in the far future (more than 10 minute)
return changed return changed

View file

@ -1,8 +1,11 @@
from lib.BitcoinECC import BitcoinECC from lib.BitcoinECC import BitcoinECC
from lib.pybitcointools import bitcoin as btctools from lib.pybitcointools import bitcoin as btctools
import logging import logging
from Config import config
# Try to load openssl # Try to load openssl
try: try:
if config.disable_openssl: raise Exception("Disabled by config")
from lib.opensslVerify import opensslVerify from lib.opensslVerify import opensslVerify
logging.info("OpenSSL loaded, version: %s" % opensslVerify.openssl_version) logging.info("OpenSSL loaded, version: %s" % opensslVerify.openssl_version)
except Exception, err: except Exception, err:

View file

@ -7,7 +7,9 @@ from util import RateLimit
FILE_BUFF = 1024*512 FILE_BUFF = 1024*512
# Request from me # Request from me
class FileRequest: class FileRequest(object):
__slots__ = ("server", "connection", "req_id", "sites", "log", "responded")
def __init__(self, server, connection): def __init__(self, server, connection):
self.server = server self.server = server
self.connection = connection self.connection = connection
@ -53,6 +55,8 @@ class FileRequest:
elif cmd == "pex": elif cmd == "pex":
self.actionPex(params) self.actionPex(params)
elif cmd == "modified":
self.actionModified(params)
elif cmd == "ping": elif cmd == "ping":
self.actionPing() self.actionPing()
else: else:
@ -158,6 +162,17 @@ class FileRequest:
self.response({"peers": packed_peers}) self.response({"peers": packed_peers})
# Get modified content.json files since
def actionModified(self, params):
site = self.sites.get(params["site"])
if not site or not site.settings["serving"]: # Site unknown or not serving
self.response({"error": "Unknown site"})
return False
modified_files = {inner_path: content["modified"] for inner_path, content in site.content_manager.contents.iteritems() if content["modified"] > params["since"]}
self.response({"modified_files": modified_files})
# Send a simple Pong! answer # Send a simple Pong! answer
def actionPing(self): def actionPing(self):
self.response("Pong!") self.response("Pong!")

View file

@ -4,7 +4,9 @@ from Config import config
from Debug import Debug from Debug import Debug
# Communicate remote peers # Communicate remote peers
class Peer: class Peer(object):
__slots__ = ("ip", "port", "site", "key", "connection_server", "connection", "last_found", "last_response", "last_ping", "added", "connection_error", "hash_failed", "download_bytes", "download_time")
def __init__(self, ip, port, site=None): def __init__(self, ip, port, site=None):
self.ip = ip self.ip = ip
self.port = port self.port = port

View file

@ -328,7 +328,7 @@ class Site:
# Add myself and get other peers from tracker # Add myself and get other peers from tracker
def announce(self, force=False): def announce(self, force=False):
if time.time() < self.last_announce+60 and not force: return # No reannouncing within 60 secs if time.time() < self.last_announce+30 and not force: return # No reannouncing within 30 secs
self.last_announce = time.time() self.last_announce = time.time()
errors = [] errors = []
address_hash = hashlib.sha1(self.address).hexdigest() address_hash = hashlib.sha1(self.address).hexdigest()

View file

@ -36,6 +36,7 @@ class SiteStorage:
def closeDb(self): def closeDb(self):
if self.db: self.db.close() if self.db: self.db.close()
self.db = None
# Return db class # Return db class
@ -120,13 +121,17 @@ class SiteStorage:
# Write content to file # Write content to file
def write(self, inner_path, content): def write(self, inner_path, content):
file_path = self.getPath(inner_path) file_path = self.getPath(inner_path)
# Create dir if not exits
file_dir = os.path.dirname(file_path)
if not os.path.isdir(file_dir):
os.makedirs(file_dir)
# Write file # Write file
if hasattr(content, 'read'): # File-like object if hasattr(content, 'read'): # File-like object
file = open(file_path, "wb") with open(file_path, "wb") as file:
shutil.copyfileobj(content, file) # Write buff to disk shutil.copyfileobj(content, file) # Write buff to disk
file.close()
else: # Simple string else: # Simple string
open(file_path, "wb").write(content) with open(file_path, "wb") as file:
file.write(content)
del content del content
self.onUpdated(inner_path) self.onUpdated(inner_path)
@ -146,7 +151,8 @@ class SiteStorage:
# Load and parse json file # Load and parse json file
def loadJson(self, inner_path): def loadJson(self, inner_path):
return json.load(self.open(inner_path)) with self.open(inner_path) as file:
return json.load(file)
# Get file size # Get file size
@ -164,7 +170,7 @@ class SiteStorage:
return os.path.isdir(self.getPath(inner_path)) return os.path.isdir(self.getPath(inner_path))
# Sercurity check and return path of site's file # Security check and return path of site's file
def getPath(self, inner_path): def getPath(self, inner_path):
inner_path = inner_path.replace("\\", "/") # Windows separator fix inner_path = inner_path.replace("\\", "/") # Windows separator fix
inner_path = re.sub("^%s/" % re.escape(self.directory), "", inner_path) # Remove site directory if begins with it inner_path = re.sub("^%s/" % re.escape(self.directory), "", inner_path) # Remove site directory if begins with it
@ -176,8 +182,6 @@ class SiteStorage:
# Verify all files sha512sum using content.json # Verify all files sha512sum using content.json
def verifyFiles(self, quick_check=False): # Fast = using file size def verifyFiles(self, quick_check=False): # Fast = using file size
bad_files = [] bad_files = []

View file

@ -39,8 +39,9 @@ class UiWSGIHandler(WSGIHandler):
if config.debug: # Allow websocket errors to appear on /Debug if config.debug: # Allow websocket errors to appear on /Debug
import sys import sys
del self.server.sockets[self.client_address] del self.server.sockets[self.client_address]
sys.modules["main"].DebugHook.handleError() sys.modules["main"].DebugHook.handleError()
del self.server.sockets[self.client_address] if self.client_address in self.server.sockets:
del self.server.sockets[self.client_address]
class UiServer: class UiServer:

Binary file not shown.

View file

@ -53,8 +53,6 @@ class Worker:
if correct == True and task["done"] == False: # Save if changed and task not done yet if correct == True and task["done"] == False: # Save if changed and task not done yet
buff.seek(0) buff.seek(0)
file_path = site.storage.getPath(task["inner_path"]) file_path = site.storage.getPath(task["inner_path"])
file_dir = os.path.dirname(file_path)
if not os.path.isdir(file_dir): os.makedirs(file_dir) # Make directory for files
site.storage.write(task["inner_path"], buff) site.storage.write(task["inner_path"], buff)
if task["done"] == False: self.manager.doneTask(task) if task["done"] == False: self.manager.doneTask(task)
task["workers_num"] -= 1 task["workers_num"] -= 1
@ -81,7 +79,7 @@ class Worker:
# Force stop the worker # Force stop the worker
def stop(self): def stop(self):
self.manager.log.debug("%s: Force stopping, thread: %s" % (self.key, self.thread)) self.manager.log.debug("%s: Force stopping, thread" % self.key)
self.running = False self.running = False
if self.thread: if self.thread:
self.thread.kill(exception=Debug.Notify("Worker stopped")) self.thread.kill(exception=Debug.Notify("Worker stopped"))

View file

@ -248,6 +248,24 @@ class Actions:
print peer.getFile(site, filename).read() print peer.getFile(site, filename).read()
print "Response time: %.3fs" % (time.time()-s) print "Response time: %.3fs" % (time.time()-s)
def peerCmd(self, peer_ip, peer_port, cmd, parameters):
logging.info("Opening a simple connection server")
global file_server
from Connection import ConnectionServer
file_server = ConnectionServer()
from Peer import Peer
peer = Peer(peer_ip, peer_port)
import json
if parameters:
parameters = json.loads(parameters.replace("'", '"'))
else:
parameters = {}
logging.info("Response: %s" % peer.request(cmd, parameters))
actions = Actions() actions = Actions()
# Starts here when running zeronet.py # Starts here when running zeronet.py
def start(): def start():

View file

@ -3,6 +3,7 @@
def main(): def main():
print "- Starting ZeroNet..." print "- Starting ZeroNet..."
import sys, os import sys, os
main = None
try: try:
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) # Imports relative to src sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) # Imports relative to src
import main import main
@ -25,7 +26,7 @@ def main():
traceback.print_exc() traceback.print_exc()
raw_input("-- Error happened, press enter to close --") raw_input("-- Error happened, press enter to close --")
if main.update_after_shutdown: # Updater if main and main.update_after_shutdown: # Updater
# Restart # Restart
gc.collect() # Garbage collect gc.collect() # Garbage collect
print "Restarting..." print "Restarting..."