mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
138 lines
4.2 KiB
Python
138 lines
4.2 KiB
Python
# -*- coding: utf-8 -
|
|
#
|
|
# This file is part of gunicorn released under the MIT license.
|
|
# See the NOTICE for more information.
|
|
|
|
|
|
import errno
|
|
import logging
|
|
import os
|
|
import select
|
|
import signal
|
|
import socket
|
|
import sys
|
|
import tempfile
|
|
import traceback
|
|
|
|
from gunicorn import http
|
|
from gunicorn import util
|
|
|
|
|
|
class Worker(object):
|
|
|
|
SIGNALS = map(
|
|
lambda x: getattr(signal, "SIG%s" % x),
|
|
"HUP QUIT INT TERM TTIN TTOU USR1".split()
|
|
)
|
|
|
|
def __init__(self, workerid, ppid, socket, app, timeout):
|
|
self.id = workerid
|
|
self.ppid = ppid
|
|
self.timeout = timeout / 2.0
|
|
fd, tmpname = tempfile.mkstemp()
|
|
self.tmp = os.fdopen(fd, "r+b")
|
|
self.tmpname = tmpname
|
|
|
|
# prevent inherientence
|
|
self.socket = socket
|
|
util.close_on_exec(self.socket)
|
|
self.socket.setblocking(0)
|
|
|
|
util.close_on_exec(fd)
|
|
|
|
self.address = self.socket.getsockname()
|
|
|
|
self.app = app
|
|
self.alive = True
|
|
self.log = logging.getLogger(__name__)
|
|
|
|
|
|
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.SIGUSR1, self.handle_quit)
|
|
|
|
def handle_quit(self, sig, frame):
|
|
self.alive = False
|
|
|
|
def handle_exit(self, sig, frame):
|
|
sys.exit(0)
|
|
|
|
def _fchmod(self, mode):
|
|
if getattr(os, 'fchmod', None):
|
|
os.fchmod(self.tmp.fileno(), mode)
|
|
else:
|
|
os.chmod(self.tmpname, mode)
|
|
|
|
def run(self):
|
|
self.init_signals()
|
|
spinner = 0
|
|
while self.alive:
|
|
|
|
nr = 0
|
|
# Accept until we hit EAGAIN. We're betting that when we're
|
|
# processing clients that more clients are waiting. When
|
|
# there's no more clients waiting we go back to the select()
|
|
# loop and wait for some lovin.
|
|
while self.alive:
|
|
try:
|
|
client, addr = self.socket.accept()
|
|
|
|
# handle connection
|
|
self.handle(client, addr)
|
|
|
|
# Update the fd mtime on each client completion
|
|
# to signal that this worker process is alive.
|
|
spinner = (spinner+1) % 2
|
|
self._fchmod(spinner)
|
|
nr += 1
|
|
except socket.error, e:
|
|
if e[0] in (errno.EAGAIN, errno.ECONNABORTED):
|
|
break # Uh oh!
|
|
|
|
raise
|
|
if nr == 0: break
|
|
|
|
while self.alive:
|
|
spinner = (spinner+1) % 2
|
|
self._fchmod(spinner)
|
|
try:
|
|
ret = select.select([self.socket], [], [],
|
|
self.timeout)
|
|
if ret[0]:
|
|
break
|
|
except select.error, e:
|
|
if e[0] == errno.EINTR:
|
|
break
|
|
raise
|
|
|
|
spinner = (spinner+1) % 2
|
|
self._fchmod(spinner)
|
|
|
|
|
|
def handle(self, client, addr):
|
|
util.close_on_exec(client)
|
|
try:
|
|
req = http.HttpRequest(client, addr, self.address)
|
|
try:
|
|
response = self.app(req.read(), req.start_response)
|
|
except Exception, e:
|
|
exc = ''.join(traceback.format_exception(*sys.exc_info()))
|
|
msg = "<h1>Internal Server Error</h1><h2>wsgi error:</h2><pre>%s</pre>" % exc
|
|
util.writelines(client,
|
|
["HTTP/1.0 500 Internal Server Error\r\n",
|
|
"Connection: close\r\n",
|
|
"Content-type: text/html\r\n",
|
|
"Content-length: %s\r\n" % str(len(msg)),
|
|
"\r\n",
|
|
msg])
|
|
return
|
|
http.HttpResponse(client, response, req).send()
|
|
except Exception, e:
|
|
self.log.exception("Error processing request. [%s]" % str(e))
|
|
finally:
|
|
util.close(client)
|
|
|