From 0935ab334497a4dfb0741a2a108de7b1c7e1198d Mon Sep 17 00:00:00 2001 From: Benoit Chesneau Date: Wed, 3 Feb 2010 13:35:32 +0100 Subject: [PATCH] branch to handle unix sockets --- gunicorn/arbiter.py | 46 +++++++++++++------- gunicorn/main.py | 28 +++++++----- gunicorn/management/commands/run_gunicorn.py | 37 +++++++++------- 3 files changed, 68 insertions(+), 43 deletions(-) diff --git a/gunicorn/arbiter.py b/gunicorn/arbiter.py index 55800144..3e5c1938 100644 --- a/gunicorn/arbiter.py +++ b/gunicorn/arbiter.py @@ -140,7 +140,7 @@ class Arbiter(object): self.log.error("should be a non GUNICORN environnement") else: raise - + for i in range(5): try: sock = self.init_socket(addr) @@ -152,30 +152,46 @@ class Arbiter(object): if i < 5: self.log.error("Retrying in 1 second.") time.sleep(1) - if self.LISTENER: - self.log.info("Listen on %s:%s" % self.LISTENER.getsockname()) - + if self.LISTENER: + try: + self.log.info("Listen on %s:%s" % self.LISTENER.getsockname()) + except TypeError: + self.log.info("Listen on %s" % self.LISTENER.getsockname()) def init_socket_fromfd(self, fd, address): - sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) - self.set_sockopts(sock) + if isinstance(address, basestring): + sock = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM) + else: + sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) + self.set_tcp_sockopts(sock) + self.set_sockopts(sock, address) return sock def init_socket(self, address): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.set_sockopts(sock) - sock.bind(address) - sock.listen(2048) + if isinstance(address, basestring): + try: + os.remove(address) + except OSError: + pass + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + else: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_tcp_sockopts(sock) + self.set_sockopts(sock, address) return sock - def set_sockopts(self, sock): - sock.setblocking(0) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + def set_tcp_sockopts(self, sock): if hasattr(socket, "TCP_CORK"): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1) elif hasattr(socket, "TCP_NOPUSH"): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NOPUSH, 1) - + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + + def set_sockopts(self, sock, address): + sock.setblocking(0) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(address) + sock.listen(2048) + def run(self): self.start() self.manage_workers() diff --git a/gunicorn/main.py b/gunicorn/main.py index a7f06d7c..b898e48b 100644 --- a/gunicorn/main.py +++ b/gunicorn/main.py @@ -28,10 +28,8 @@ UMASK = 0 def options(): return [ - op.make_option('--host', dest='host', - help='Host to listen on. [%default]'), - op.make_option('--port', dest='port', type='int', - help='Port to listen on. [%default]'), + op.make_option('-b', '--bind', dest='bind', + help='Adress to listen on. Ex. host:port or unix://path/to/socketfile'), op.make_option('--workers', dest='workers', type='int', help='Number of workers to spawn. [%default]'), op.make_option('-p','--pid', dest='pidfile', @@ -95,21 +93,26 @@ def main(usage, get_app): if opts.debug: workers = 1 - host = opts.host or '127.0.0.1' - port = opts.port - if port is None: - if ':' in host: - host, port = host.split(':', 1) + bind = opts.bind or '127.0.0.1' + if bind.startswith("unix:"): + addr = bind.split("unix:")[1] + else: + if ':' in bind: + host, port = bind.split(':', 1) + if not port.isdigit(): + raise RuntimeError("%r is not a valid port number." % port) port = int(port) else: + host = bind port = 8000 + addr = (host, port) kwargs = dict( debug=opts.debug, pidfile=opts.pidfile ) - arbiter = Arbiter((host,port), workers, app, + arbiter = Arbiter(addr, workers, app, **kwargs) if opts.daemon: daemonize() @@ -229,8 +232,9 @@ def run_paster(): else: workers = int(ctx.local_conf.get('workers', 1)) - opts.host = opts.host or ctx.local_conf.get('host', '127.0.0.1') - opts.port = opts.port or int(ctx.local_conf.get('port', 8000)) + host = opts.host or ctx.local_conf.get('host', '127.0.0.1') + port = opts.port or int(ctx.local_conf.get('port', 8000)) + bind = "%s:%s" % (host, port) debug = ctx.global_conf.get('debug') == "true" if debug: diff --git a/gunicorn/management/commands/run_gunicorn.py b/gunicorn/management/commands/run_gunicorn.py index f19f321b..e0115286 100644 --- a/gunicorn/management/commands/run_gunicorn.py +++ b/gunicorn/management/commands/run_gunicorn.py @@ -30,7 +30,7 @@ class Command(BaseCommand): help='Run daemonized in the background.'), ) help = "Starts a fully-functional Web server using gunicorn." - args = '[optional port number, or ipaddr:port]' + args = '[optional port number, or ipaddr:port or unix:/path/to/sockfile]' # Validation is called explicitly each time the server is reloaded. requires_model_validation = False @@ -38,20 +38,21 @@ class Command(BaseCommand): def handle(self, addrport='', *args, **options): if args: raise CommandError('Usage is runserver %s' % self.args) - if not addrport: - addr = '' - port = '8000' + + bind = addrport or '127.0.0.1' + if bind.startswith("unix:"): + addr = bind.split("unix:")[1] else: - try: - addr, port = addrport.split(':') - except ValueError: - addr, port = '', addrport - if not addr: - addr = '127.0.0.1' - - if not port.isdigit(): - raise CommandError("%r is not a valid port number." % port) - + if ':' in bind: + host, port = host.split(':', 1) + if not port.isdigit(): + raise CommandError("%r is not a valid port number." % port) + port = int(port) + else: + host = bind + port = 8000 + addr = (host, port) + admin_media_path = options.get('admin_media_path', '') workers = int(options.get('workers', '1')) daemon = options.get('daemon') @@ -61,7 +62,11 @@ class Command(BaseCommand): print "Validating models..." self.validate(display_num_errors=True) print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE) - print "Development server is running at http://%s:%s/" % (addr, port) + + if isinstance(address, basestring): + print "Development server is running at unix:/" % addr + else: + print "Development server is running at http://%s:%s/" % addr print "Quit the server with %s." % quit_command # django.core.management.base forces the locale to en-us. @@ -69,7 +74,7 @@ class Command(BaseCommand): try: handler = AdminMediaHandler(WSGIHandler(), admin_media_path) - arbiter = Arbiter((addr, int(port)), workers, handler, + arbiter = Arbiter(addr, workers, handler, pidfile=pidfile) if daemon: daemonize()