allows gunicorn to bind to multiple address

Allows gunicorn to listen on different interface. It can be either ipv6,
unix or ipv4 sockets.

Ex:

    gunicorn -w3 -b 127.0.0.1:8001 -b 127.0.0.1:8000 -b [::1]:8000 test:app

fix #444
This commit is contained in:
benoitc 2012-12-11 08:42:12 +01:00
parent 41a6999fa3
commit b7b51adf13
12 changed files with 191 additions and 140 deletions

View File

@ -12,8 +12,7 @@ error_email_from = paste@localhost
[server:main] [server:main]
use = egg:gunicorn#main use = egg:gunicorn#main
host = 127.0.0.1 host = 127.0.0.1:8000, 127.0.0.1:8001
port = 5000
workers = 2 workers = 2
[app:main] [app:main]

View File

@ -29,7 +29,7 @@ class PasterBaseApplication(Application):
if host and port: if host and port:
cfg['bind'] = '%s:%s' % (host, port) cfg['bind'] = '%s:%s' % (host, port)
elif host: elif host:
cfg['bind'] = host cfg['bind'] = host.split(',')
cfg['workers'] = int(lc.get('workers', 1)) cfg['workers'] = int(lc.get('workers', 1))
cfg['umask'] = int(lc.get('umask', 0)) cfg['umask'] = int(lc.get('umask', 0))
@ -55,19 +55,11 @@ class PasterBaseApplication(Application):
parser = ConfigParser.ConfigParser() parser = ConfigParser.ConfigParser()
parser.read([self.cfgfname]) parser.read([self.cfgfname])
if parser.has_section('loggers'): if parser.has_section('loggers'):
if sys.version_info >= (2, 6): from logging.config import fileConfig
from logging.config import fileConfig
else:
# Use our custom fileConfig -- 2.5.1's with a custom Formatter class
# and less strict whitespace (which were incorporated into 2.6's)
from gunicorn.logging_config import fileConfig
config_file = os.path.abspath(self.cfgfname) config_file = os.path.abspath(self.cfgfname)
fileConfig(config_file, dict(__file__=config_file, fileConfig(config_file, dict(__file__=config_file,
here=os.path.dirname(config_file))) here=os.path.dirname(config_file)))
class PasterApplication(PasterBaseApplication): class PasterApplication(PasterBaseApplication):
def init(self, parser, opts, args): def init(self, parser, opts, args):
@ -111,7 +103,7 @@ class PasterServerApplication(PasterBaseApplication):
bind = "%s:%s" % (host, port) bind = "%s:%s" % (host, port)
else: else:
bind = host bind = host
cfg["bind"] = bind cfg["bind"] = bind.split(',')
if gcfg: if gcfg:
for k, v in gcfg.items(): for k, v in gcfg.items():

View File

@ -16,7 +16,7 @@ import traceback
from gunicorn.errors import HaltServer from gunicorn.errors import HaltServer
from gunicorn.pidfile import Pidfile from gunicorn.pidfile import Pidfile
from gunicorn.sock import create_socket from gunicorn.sock import create_sockets
from gunicorn import util from gunicorn import util
from gunicorn import __version__, SERVER_SOFTWARE from gunicorn import __version__, SERVER_SOFTWARE
@ -35,7 +35,7 @@ class Arbiter(object):
START_CTX = {} START_CTX = {}
LISTENER = None LISTENERS = []
WORKERS = {} WORKERS = {}
PIPE = [] PIPE = []
@ -120,12 +120,12 @@ class Arbiter(object):
self.pidfile.create(self.pid) self.pidfile.create(self.pid)
self.cfg.on_starting(self) self.cfg.on_starting(self)
self.init_signals() self.init_signals()
if not self.LISTENER: if not self.LISTENERS:
self.LISTENER = create_socket(self.cfg, self.log) self.LISTENERS = create_sockets(self.cfg, self.log)
listeners_str = ",".join([str(l) for l in self.LISTENERS])
self.log.debug("Arbiter booted") self.log.debug("Arbiter booted")
self.log.info("Listening at: %s (%s)", self.LISTENER, self.log.info("Listening at: %s (%s)", listeners_str, self.pid)
self.pid)
self.log.info("Using worker: %s", self.log.info("Using worker: %s",
self.cfg.settings['worker_class'].get()) self.cfg.settings['worker_class'].get())
@ -321,11 +321,13 @@ class Arbiter(object):
:attr graceful: boolean, If True (the default) workers will be :attr graceful: boolean, If True (the default) workers will be
killed gracefully (ie. trying to wait for the current connection) killed gracefully (ie. trying to wait for the current connection)
""" """
try: for l in self.LISTENERS:
self.LISTENER.close() try:
except Exception: l.close()
pass except Exception:
self.LISTENER = None pass
self.LISTENERS = []
sig = signal.SIGQUIT sig = signal.SIGQUIT
if not graceful: if not graceful:
sig = signal.SIGTERM sig = signal.SIGTERM
@ -348,11 +350,16 @@ class Arbiter(object):
self.master_name = "Old Master" self.master_name = "Old Master"
return return
os.environ['GUNICORN_FD'] = str(self.LISTENER.fileno()) fds = [l.fileno() for l in self.LISTENERS]
os.environ['GUNICORN_FD'] = ",".join([str(fd) for fd in fds])
os.chdir(self.START_CTX['cwd']) os.chdir(self.START_CTX['cwd'])
self.cfg.pre_exec(self) self.cfg.pre_exec(self)
util.closerange(3, self.LISTENER.fileno())
util.closerange(self.LISTENER.fileno()+1, util.get_maxfd()) # close all file descriptors except bound sockets
util.closerange(3, fds[0])
util.closerange(fds[-1]+1, util.get_maxfd())
os.execvpe(self.START_CTX[0], self.START_CTX['args'], os.environ) os.execvpe(self.START_CTX[0], self.START_CTX['args'], os.environ)
def reload(self): def reload(self):
@ -367,9 +374,11 @@ class Arbiter(object):
# do we need to change listener ? # do we need to change listener ?
if old_address != self.cfg.address: if old_address != self.cfg.address:
self.LISTENER.close() # close all listeners
self.LISTENER = create_socket(self.cfg, self.log) [l.close for l in self.LISTENERS]
self.log.info("Listening at: %s", self.LISTENER) # init new listeners
self.LISTENERS = create_sockeSt(self.cfg, self.log)
self.log.info("Listening at: %s", ",".join(str(self.LISTENERS)))
# do some actions on reload # do some actions on reload
self.cfg.on_reload(self) self.cfg.on_reload(self)
@ -451,7 +460,7 @@ class Arbiter(object):
def spawn_worker(self): def spawn_worker(self):
self.worker_age += 1 self.worker_age += 1
worker = self.worker_class(self.worker_age, self.pid, self.LISTENER, worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS,
self.app, self.timeout/2.0, self.app, self.timeout/2.0,
self.cfg, self.log) self.cfg, self.log)
self.cfg.pre_fork(self, worker) self.cfg.pre_fork(self, worker)

View File

@ -86,8 +86,8 @@ class Config(object):
@property @property
def address(self): def address(self):
bind = self.settings['bind'].get() s = self.settings['bind'].get()
return util.parse_address(bytes_to_str(bind)) return [util.parse_address(bytes_to_str(bind)) for bind in s]
@property @property
def uid(self): def uid(self):
@ -220,6 +220,16 @@ def validate_string(val):
raise TypeError("Not a string: %s" % val) raise TypeError("Not a string: %s" % val)
return val.strip() return val.strip()
def validate_list_string(val):
if not val:
return []
# legacy syntax
if isinstance(val, string_types):
val = [val]
return [validate_string(v) for v in val]
def validate_string_to_list(val): def validate_string_to_list(val):
val = validate_string(val) val = validate_string(val)
@ -315,15 +325,16 @@ class ConfigFile(Setting):
class Bind(Setting): class Bind(Setting):
name = "bind" name = "bind"
action = "append"
section = "Server Socket" section = "Server Socket"
cli = ["-b", "--bind"] cli = ["-b", "--bind"]
meta = "ADDRESS" meta = "ADDRESS"
validator = validate_string validator = validate_list_string
if 'PORT' in os.environ: if 'PORT' in os.environ:
default = '0.0.0.0:{0}'.format(os.environ.get('PORT')) default = ['0.0.0.0:{0}'.format(os.environ.get('PORT'))]
else: else:
default = '127.0.0.1:8000' default = ['127.0.0.1:8000']
desc = """\ desc = """\
The socket to bind. The socket to bind.

View File

@ -14,10 +14,11 @@ from gunicorn.six import string_types
class BaseSocket(object): class BaseSocket(object):
def __init__(self, conf, log, fd=None): def __init__(self, address, conf, log, fd=None):
self.log = log self.log = log
self.conf = conf self.conf = conf
self.address = conf.address
self.cfg_addr = address
if fd is None: if fd is None:
sock = socket.socket(self.FAMILY, socket.SOCK_STREAM) sock = socket.socket(self.FAMILY, socket.SOCK_STREAM)
else: else:
@ -39,7 +40,7 @@ class BaseSocket(object):
return sock return sock
def bind(self, sock): def bind(self, sock):
sock.bind(self.address) sock.bind(self.cfg_addr)
def close(self): def close(self):
try: try:
@ -72,37 +73,28 @@ class UnixSocket(BaseSocket):
FAMILY = socket.AF_UNIX FAMILY = socket.AF_UNIX
def __init__(self, conf, log, fd=None): def __init__(self, address, log, fd=None):
if fd is None: if fd is None:
try: try:
os.remove(conf.address) os.remove(address)
except OSError: except OSError:
pass pass
super(UnixSocket, self).__init__(conf, log, fd=fd) super(UnixSocket, self).__init__(addr, log, fd=fd)
def __str__(self): def __str__(self):
return "unix:%s" % self.address return "unix:%s" % self.cfg_addr
def bind(self, sock): def bind(self, sock):
old_umask = os.umask(self.conf.umask) old_umask = os.umask(self.conf.umask)
sock.bind(self.address) sock.bind(self.cfg_addr)
util.chown(self.address, self.conf.uid, self.conf.gid) util.chown(self.cfg_addr, self.conf.uid, self.conf.gid)
os.umask(old_umask) os.umask(old_umask)
def close(self): def close(self):
super(UnixSocket, self).close() super(UnixSocket, self).close()
os.unlink(self.address) os.unlink(self.cfg_addr)
def create_socket(conf, log):
"""
Create a new socket for the given address. If the
address is a tuple, a TCP socket is created. If it
is a string, a Unix socket is created. Otherwise
a TypeError is raised.
"""
# get it only once
addr = conf.address
def _sock_type(addr):
if isinstance(addr, tuple): if isinstance(addr, tuple):
if util.is_ipv6(addr[0]): if util.is_ipv6(addr[0]):
sock_type = TCP6Socket sock_type = TCP6Socket
@ -112,33 +104,63 @@ def create_socket(conf, log):
sock_type = UnixSocket sock_type = UnixSocket
else: else:
raise TypeError("Unable to create socket from: %r" % addr) raise TypeError("Unable to create socket from: %r" % addr)
return sock_type
def create_sockets(conf, log):
"""
Create a new socket for the given address. If the
address is a tuple, a TCP socket is created. If it
is a string, a Unix socket is created. Otherwise
a TypeError is raised.
"""
# get it only once
laddr = conf.address
listeners = []
# sockets are already bound
if 'GUNICORN_FD' in os.environ: if 'GUNICORN_FD' in os.environ:
fd = int(os.environ.pop('GUNICORN_FD')) fds = os.environ.pop('GUNICORN_FD').split(',')
try: for i, fd in enumerate(fds):
return sock_type(conf, log, fd=fd) fd = int(fd)
except socket.error as e: addr = laddr[i]
if e.args[0] == errno.ENOTCONN: sock_type = _sock_type(addr)
log.error("GUNICORN_FD should refer to an open socket.")
try:
listeners.append(sock_type(addr, conf, log, fd=fd))
except socket.error as e:
if e.args[0] == errno.ENOTCONN:
log.error("GUNICORN_FD should refer to an open socket.")
else:
raise
return listeners
# no sockets is bound, first initialization of gunicorn in this env.
for addr in laddr:
sock_type = _sock_type(addr)
# If we fail to create a socket from GUNICORN_FD
# we fall through and try and open the socket
# normally.
sock = None
for i in range(5):
try:
sock = sock_type(addr, conf, log)
except socket.error as e:
if e.args[0] == errno.EADDRINUSE:
log.error("Connection in use: %s", str(addr))
if e.args[0] == errno.EADDRNOTAVAIL:
log.error("Invalid address: %s", str(addr))
sys.exit(1)
if i < 5:
log.error("Retrying in 1 second.")
time.sleep(1)
else: else:
raise break
# If we fail to create a socket from GUNICORN_FD if sock is None:
# we fall through and try and open the socket log.error("Can't connect to %s", str(addr))
# normally. sys.exit(1)
for i in range(5): listeners.append(sock)
try:
return sock_type(conf, log)
except socket.error as e:
if e.args[0] == errno.EADDRINUSE:
log.error("Connection in use: %s", str(addr))
if e.args[0] == errno.EADDRNOTAVAIL:
log.error("Invalid address: %s", str(addr))
sys.exit(1)
if i < 5:
log.error("Retrying in 1 second.")
time.sleep(1)
log.error("Can't connect to %s", str(addr)) return listeners
sys.exit(1)

View File

@ -26,14 +26,14 @@ class AsyncWorker(base.Worker):
def timeout_ctx(self): def timeout_ctx(self):
raise NotImplementedError() raise NotImplementedError()
def handle(self, client, addr): def handle(self, listener, client, addr):
req = None req = None
try: try:
parser = http.RequestParser(self.cfg, client) parser = http.RequestParser(self.cfg, client)
try: try:
if not self.cfg.keepalive: if not self.cfg.keepalive:
req = six.next(parser) req = six.next(parser)
self.handle_request(req, client, addr) self.handle_request(listener, req, client, addr)
else: else:
# keepalive loop # keepalive loop
while True: while True:
@ -42,7 +42,7 @@ class AsyncWorker(base.Worker):
req = six.next(parser) req = six.next(parser)
if not req: if not req:
break break
self.handle_request(req, client, addr) self.handle_request(listener, req, client, addr)
except http.errors.NoMoreData as e: except http.errors.NoMoreData as e:
self.log.debug("Ignored premature client disconnection. %s", e) self.log.debug("Ignored premature client disconnection. %s", e)
except StopIteration as e: except StopIteration as e:
@ -64,11 +64,12 @@ class AsyncWorker(base.Worker):
finally: finally:
util.close(client) util.close(client)
def handle_request(self, req, sock, addr): def handle_request(self, listener, req, sock, addr):
request_start = datetime.now() request_start = datetime.now()
try: try:
self.cfg.pre_request(self, req) self.cfg.pre_request(self, req)
resp, environ = wsgi.create(req, sock, addr, self.address, self.cfg) resp, environ = wsgi.create(req, sock, addr,
listener.getsockname(), self.cfg)
self.nr += 1 self.nr += 1
if self.alive and self.nr >= self.max_requests: if self.alive and self.nr >= self.max_requests:
self.log.info("Autorestarting worker after current request.") self.log.info("Autorestarting worker after current request.")

View File

@ -26,7 +26,7 @@ class Worker(object):
PIPE = [] PIPE = []
def __init__(self, age, ppid, socket, app, timeout, cfg, log): def __init__(self, age, ppid, sockets, app, timeout, cfg, log):
"""\ """\
This is called pre-fork so it shouldn't do anything to the This is called pre-fork so it shouldn't do anything to the
current process. If there's a need to make process wide current process. If there's a need to make process wide
@ -34,7 +34,7 @@ class Worker(object):
""" """
self.age = age self.age = age
self.ppid = ppid self.ppid = ppid
self.socket = socket self.sockets = sockets
self.app = app self.app = app
self.timeout = timeout self.timeout = timeout
self.cfg = cfg self.cfg = cfg
@ -45,7 +45,6 @@ class Worker(object):
self.alive = True self.alive = True
self.log = log self.log = log
self.debug = cfg.debug self.debug = cfg.debug
self.address = self.socket.getsockname()
self.tmp = WorkerTmp(cfg) self.tmp = WorkerTmp(cfg)
def __str__(self): def __str__(self):
@ -90,7 +89,7 @@ class Worker(object):
util.close_on_exec(p) util.close_on_exec(p)
# Prevent fd inherientence # Prevent fd inherientence
util.close_on_exec(self.socket) [util.close_on_exec(s) for s in self.sockets]
util.close_on_exec(self.tmp.fileno()) util.close_on_exec(self.tmp.fileno())
self.log.close_on_exec() self.log.close_on_exec()

View File

@ -3,10 +3,9 @@
# This file is part of gunicorn released under the MIT license. # This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information. # See the NOTICE for more information.
from __future__ import with_statement from functools import partial
import os import os
try: try:
import eventlet import eventlet
except ImportError: except ImportError:
@ -33,10 +32,13 @@ class EventletWorker(AsyncWorker):
return eventlet.Timeout(self.cfg.keepalive or None, False) return eventlet.Timeout(self.cfg.keepalive or None, False)
def run(self): def run(self):
self.socket = GreenSocket(family_or_realsock=self.socket.sock) acceptors = []
self.socket.setblocking(1) for sock in self.sockets:
self.acceptor = eventlet.spawn(eventlet.serve, self.socket, s = GreenSocket(family_or_realsock=sock)
self.handle, self.worker_connections) s.setblocking(1)
hfun = partial(self.handle, s)
acceptor = eventlet.spawn(eventlet.serve, s, hfun,
self.worker_connections)
while self.alive: while self.alive:
self.notify() self.notify()
@ -48,4 +50,4 @@ class EventletWorker(AsyncWorker):
self.notify() self.notify()
with eventlet.Timeout(self.cfg.graceful_timeout, False): with eventlet.Timeout(self.cfg.graceful_timeout, False):
eventlet.kill(self.acceptor, eventlet.StopServe) [eventlet.kill(a, eventlet.StopServe) for a in acceptors]

View File

@ -8,6 +8,7 @@ from __future__ import with_statement
import os import os
import sys import sys
from datetime import datetime from datetime import datetime
from functools import partial
import time import time
# workaround on osx, disable kqueue # workaround on osx, disable kqueue
@ -51,18 +52,23 @@ class GeventWorker(AsyncWorker):
def timeout_ctx(self): def timeout_ctx(self):
return gevent.Timeout(self.cfg.keepalive, False) return gevent.Timeout(self.cfg.keepalive, False)
def run(self): def run(self):
self.socket.setblocking(1) servers = []
for s in self.sockets:
s.setblocking(1)
pool = Pool(self.worker_connections)
if self.server_class is not None:
server = self.server_class(
s, application=self.wsgi, spawn=pool, log=self.log,
handler_class=self.wsgi_handler)
else:
hfun = partial(self.handle, s)
server = StreamServer(s, handle=hfun, spawn=pool)
pool = Pool(self.worker_connections) server.start()
if self.server_class is not None: servers.append(server)
server = self.server_class(
self.socket, application=self.wsgi, spawn=pool, log=self.log,
handler_class=self.wsgi_handler)
else:
server = StreamServer(self.socket, handle=self.handle, spawn=pool)
server.start()
pid = os.getpid() pid = os.getpid()
try: try:
while self.alive: while self.alive:
@ -79,13 +85,19 @@ class GeventWorker(AsyncWorker):
try: try:
# Stop accepting requests # Stop accepting requests
server.kill() [server.stop_accepting() for server in servers]
# Handle current requests until graceful_timeout # Handle current requests until graceful_timeout
ts = time.time() ts = time.time()
while time.time() - ts <= self.cfg.graceful_timeout: while time.time() - ts <= self.cfg.graceful_timeout:
if server.pool.free_count() == server.pool.size: accepting = 0
return # all requests was handled for server in servers:
if server.pool.free_count() != server.pool.size:
accepting += 1
# if no server is accepting a connection, we can exit
if not accepting:
return
self.notify() self.notify()
gevent.sleep(1.0) gevent.sleep(1.0)

View File

@ -50,7 +50,6 @@ class TornadoWorker(Worker):
self.ioloop.stop() self.ioloop.stop()
def run(self): def run(self):
self.socket.setblocking(0)
self.ioloop = IOLoop.instance() self.ioloop = IOLoop.instance()
self.alive = True self.alive = True
PeriodicCallback(self.watchdog, 1000, io_loop=self.ioloop).start() PeriodicCallback(self.watchdog, 1000, io_loop=self.ioloop).start()
@ -77,16 +76,15 @@ class TornadoWorker(Worker):
sys.modules["tornado.httpserver"] = httpserver sys.modules["tornado.httpserver"] = httpserver
server = tornado.httpserver.HTTPServer(app, io_loop=self.ioloop) server = tornado.httpserver.HTTPServer(app, io_loop=self.ioloop)
if hasattr(server, "add_socket"): # tornado > 2.0 for s in self.sockets:
server.add_socket(self.socket) s.setblocking(0)
elif hasattr(server, "_sockets"): # tornado 2.0 if hasattr(server, "add_socket"): # tornado > 2.0
server._sockets[self.socket.fileno()] = self.socket server.add_socket(s)
else: # tornado 1.2 or earlier elif hasattr(server, "_sockets"): # tornado 2.0
server._socket = self.socket server._sockets[s.fileno()] = s
server.no_keep_alive = self.cfg.keepalive <= 0 server.no_keep_alive = self.cfg.keepalive <= 0
server.xheaders = bool(self.cfg.x_forwarded_for_header) server.xheaders = bool(self.cfg.x_forwarded_for_header)
server.start(num_processes=1) server.start(num_processes=1)
self.ioloop.start() self.ioloop.start()

View File

@ -21,8 +21,9 @@ class SyncWorker(base.Worker):
def run(self): def run(self):
# self.socket appears to lose its blocking status after # self.socket appears to lose its blocking status after
# we fork in the arbiter. Reset it here. # we fork in the arbiter. Reset it here.
self.socket.setblocking(0) [s.setblocking(0) for s in self.sockets]
ready = self.sockets
while self.alive: while self.alive:
self.notify() self.notify()
@ -30,20 +31,23 @@ class SyncWorker(base.Worker):
# that no connection is waiting we fall down to the # that no connection is waiting we fall down to the
# select which is where we'll wait for a bit for new # select which is where we'll wait for a bit for new
# workers to come give us some love. # workers to come give us some love.
try:
client, addr = self.socket.accept()
client.setblocking(1)
util.close_on_exec(client)
self.handle(client, addr)
# Keep processing clients until no one is waiting. This for sock in ready:
# prevents the need to select() for every client that we try:
# process. client, addr = sock.accept()
continue client.setblocking(1)
util.close_on_exec(client)
self.handle(sock, client, addr)
except socket.error as e: # Keep processing clients until no one is waiting. This
if e.args[0] not in (errno.EAGAIN, errno.ECONNABORTED): # prevents the need to select() for every client that we
raise # process.
continue
except socket.error as e:
if e.args[0] not in (errno.EAGAIN, errno.ECONNABORTED,
errno.EWOULDBLOCK):
raise
# If our parent changed then we shut down. # If our parent changed then we shut down.
if self.ppid != os.getppid(): if self.ppid != os.getppid():
@ -52,25 +56,28 @@ class SyncWorker(base.Worker):
try: try:
self.notify() self.notify()
ret = select.select([self.socket], [], self.PIPE, self.timeout) ret = select.select(self.sockets, [], self.PIPE, self.timeout)
if ret[0]: if ret[0]:
ready = ret[0]
continue continue
except select.error as e: except select.error as e:
if e.args[0] == errno.EINTR: if e.args[0] == errno.EINTR:
ready = self.sockets
continue continue
if e.args[0] == errno.EBADF: if e.args[0] == errno.EBADF:
if self.nr < 0: if self.nr < 0:
ready = self.sockets
continue continue
else: else:
return return
raise raise
def handle(self, client, addr): def handle(self, listener, client, addr):
req = None req = None
try: try:
parser = http.RequestParser(self.cfg, client) parser = http.RequestParser(self.cfg, client)
req = six.next(parser) req = six.next(parser)
self.handle_request(req, client, addr) self.handle_request(listener, req, client, addr)
except http.errors.NoMoreData as e: except http.errors.NoMoreData as e:
self.log.debug("Ignored premature client disconnection. %s", e) self.log.debug("Ignored premature client disconnection. %s", e)
except StopIteration as e: except StopIteration as e:
@ -85,13 +92,13 @@ class SyncWorker(base.Worker):
finally: finally:
util.close(client) util.close(client)
def handle_request(self, req, client, addr): def handle_request(self, listener, req, client, addr):
environ = {} environ = {}
try: try:
self.cfg.pre_request(self, req) self.cfg.pre_request(self, req)
request_start = datetime.now() request_start = datetime.now()
resp, environ = wsgi.create(req, client, addr, resp, environ = wsgi.create(req, client, addr,
self.address, self.cfg) listener.getsockname(), self.cfg)
# Force the connection closed until someone shows # Force the connection closed until someone shows
# a buffering proxy that supports Keep-Alive to # a buffering proxy that supports Keep-Alive to
# the backend. # the backend.
@ -124,4 +131,3 @@ class SyncWorker(base.Worker):
self.cfg.post_request(self, req, environ) self.cfg.post_request(self, req, environ)
except: except:
pass pass

View File

@ -63,7 +63,7 @@ def test_property_access():
t.eq(c.workers, 3) t.eq(c.workers, 3)
# Address is parsed # Address is parsed
t.eq(c.address, ("127.0.0.1", 8000)) t.eq(c.address, [("127.0.0.1", 8000)])
# User and group defaults # User and group defaults
t.eq(os.geteuid(), c.uid) t.eq(os.geteuid(), c.uid)
@ -165,7 +165,7 @@ def test_callable_validation_for_string():
def test_cmd_line(): def test_cmd_line():
with AltArgs(["prog_name", "-b", "blargh"]): with AltArgs(["prog_name", "-b", "blargh"]):
app = NoConfigApp() app = NoConfigApp()
t.eq(app.cfg.bind, "blargh") t.eq(app.cfg.bind, ["blargh"])
with AltArgs(["prog_name", "-w", "3"]): with AltArgs(["prog_name", "-w", "3"]):
app = NoConfigApp() app = NoConfigApp()
t.eq(app.cfg.workers, 3) t.eq(app.cfg.workers, 3)
@ -183,12 +183,12 @@ def test_app_config():
def test_load_config(): def test_load_config():
with AltArgs(["prog_name", "-c", cfg_file()]): with AltArgs(["prog_name", "-c", cfg_file()]):
app = NoConfigApp() app = NoConfigApp()
t.eq(app.cfg.bind, "unix:/tmp/bar/baz") t.eq(app.cfg.bind, ["unix:/tmp/bar/baz"])
t.eq(app.cfg.workers, 3) t.eq(app.cfg.workers, 3)
t.eq(app.cfg.proc_name, "fooey") t.eq(app.cfg.proc_name, "fooey")
def test_cli_overrides_config(): def test_cli_overrides_config():
with AltArgs(["prog_name", "-c", cfg_file(), "-b", "blarney"]): with AltArgs(["prog_name", "-c", cfg_file(), "-b", "blarney"]):
app = NoConfigApp() app = NoConfigApp()
t.eq(app.cfg.bind, "blarney") t.eq(app.cfg.bind, ["blarney"])
t.eq(app.cfg.proc_name, "fooey") t.eq(app.cfg.proc_name, "fooey")