mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
153 lines
4.6 KiB
Python
153 lines
4.6 KiB
Python
# -*- coding: utf-8 -
|
|
#
|
|
# This file is part of gunicorn released under the MIT license.
|
|
# See the NOTICE for more information.
|
|
|
|
|
|
import logging
|
|
import os
|
|
import random
|
|
import signal
|
|
import sys
|
|
import tempfile
|
|
import traceback
|
|
|
|
|
|
from gunicorn import util
|
|
from gunicorn.workers.workertmp import WorkerTmp
|
|
|
|
from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, \
|
|
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion
|
|
|
|
|
|
class Worker(object):
|
|
|
|
SIGNALS = map(
|
|
lambda x: getattr(signal, "SIG%s" % x),
|
|
"HUP QUIT INT TERM USR1 USR2 WINCH CHLD".split()
|
|
)
|
|
|
|
PIPE = []
|
|
|
|
def __init__(self, age, ppid, socket, app, timeout, cfg):
|
|
"""\
|
|
This is called pre-fork so it shouldn't do anything to the
|
|
current process. If there's a need to make process wide
|
|
changes you'll want to do that in ``self.init_process()``.
|
|
"""
|
|
self.age = age
|
|
self.ppid = ppid
|
|
self.socket = socket
|
|
self.app = app
|
|
self.timeout = timeout
|
|
self.cfg = cfg
|
|
self.booted = False
|
|
|
|
self.nr = 0
|
|
self.max_requests = cfg.max_requests or sys.maxint
|
|
self.alive = True
|
|
self.log = logging.getLogger(__name__)
|
|
self.debug = cfg.debug
|
|
self.address = self.socket.getsockname()
|
|
self.tmp = WorkerTmp()
|
|
|
|
def __str__(self):
|
|
return "<Worker %s>" % self.pid
|
|
|
|
@property
|
|
def pid(self):
|
|
return os.getpid()
|
|
|
|
def notify(self):
|
|
"""\
|
|
Your worker subclass must arrange to have this method called
|
|
once every ``self.timeout`` seconds. If you fail in accomplishing
|
|
this task, the master process will murder your workers.
|
|
"""
|
|
self.tmp.notify()
|
|
|
|
def run(self):
|
|
"""\
|
|
This is the mainloop of a worker process. You should override
|
|
this method in a subclass to provide the intended behaviour
|
|
for your particular evil schemes.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def init_process(self):
|
|
"""\
|
|
If you override this method in a subclass, the last statement
|
|
in the function should be to call this method with
|
|
super(MyWorkerClass, self).init_process() so that the ``run()``
|
|
loop is initiated.
|
|
"""
|
|
util.set_owner_process(self.cfg.uid, self.cfg.gid)
|
|
|
|
# Reseed the random number generator
|
|
random.seed()
|
|
|
|
# For waking ourselves up
|
|
self.PIPE = os.pipe()
|
|
map(util.set_non_blocking, self.PIPE)
|
|
map(util.close_on_exec, self.PIPE)
|
|
|
|
# Prevent fd inherientence
|
|
util.close_on_exec(self.socket)
|
|
util.close_on_exec(self.tmp.fileno())
|
|
self.init_signals()
|
|
|
|
self.wsgi = self.app.wsgi()
|
|
|
|
# Enter main run loop
|
|
self.booted = True
|
|
self.run()
|
|
|
|
def init_signals(self):
|
|
map(lambda s: signal.signal(s, signal.SIG_DFL), self.SIGNALS)
|
|
signal.signal(signal.SIGQUIT, self.handle_quit)
|
|
signal.signal(signal.SIGTERM, self.handle_exit)
|
|
signal.signal(signal.SIGINT, self.handle_exit)
|
|
signal.signal(signal.SIGWINCH, self.handle_winch)
|
|
|
|
def handle_quit(self, sig, frame):
|
|
self.alive = False
|
|
|
|
def handle_exit(self, sig, frame):
|
|
self.alive = False
|
|
sys.exit(0)
|
|
|
|
|
|
def handle_error(self, client, exc):
|
|
|
|
if isinstance(exc, (InvalidRequestLine, InvalidRequestMethod,
|
|
InvalidHTTPVersion, InvalidHeader, InvalidHeaderName,)):
|
|
|
|
if isinstance(exc, InvalidRequestLine):
|
|
mesg = "<p>Invalid Request Line '%s'</p>" % str(exc)
|
|
elif isinstance(exc, InvalidRequestMethod):
|
|
mesg = "<p>Invalid Method'%s'</p>" % str(exc)
|
|
elif isinstance(exc, InvalidHTTPVersion):
|
|
mesg = "<p>Invalid HTTP Version '%s'</p>" % str(exc)
|
|
elif isinstance(exc, (InvalidHeaderName, InvalidHeader,)):
|
|
mesg = "<p>Invalid Header'%s'</p>" % str(exc)
|
|
reason = "Bad Request"
|
|
status_int = 400
|
|
else:
|
|
mesg = reason = "Internal Server reason"
|
|
status_int = 500
|
|
|
|
if self.debug:
|
|
tb = traceback.format_exc()
|
|
mesg += "<h2>Traceback:</23><pre>%s</pre>" % tb
|
|
|
|
try:
|
|
util.write_error(client, mesg, status_int=status_int,
|
|
reason=reason)
|
|
except:
|
|
self.log.warning("Unexpected error" % traceback.format_exc())
|
|
pass
|
|
|
|
def handle_winch(self, sig, fname):
|
|
# Ignore SIGWINCH in worker. Fixes a crash on OpenBSD.
|
|
return
|