mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
lot of fixes. nonblocking socket need to be handled correctly though.
This commit is contained in:
parent
7fccf32c06
commit
cb79c8514d
@ -99,6 +99,11 @@ class Arbiter(object):
|
|||||||
sock.setblocking(0)
|
sock.setblocking(0)
|
||||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
|
||||||
|
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.bind(address)
|
sock.bind(address)
|
||||||
sock.listen(2048)
|
sock.listen(2048)
|
||||||
return sock
|
return sock
|
||||||
|
|||||||
@ -22,20 +22,26 @@ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, \
|
|||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
SOCKET_CLOSED = (ECONNRESET, ENOTCONN, ESHUTDOWN)
|
||||||
|
|
||||||
class IOStream(object):
|
class IOStream(object):
|
||||||
|
|
||||||
chunk_size = 4096
|
chunk_size = 4096
|
||||||
|
|
||||||
def __init__(self, sock):
|
def __init__(self, sock):
|
||||||
self.sock = sock
|
self.sock = sock.dup()
|
||||||
|
self.sock.setblocking(0)
|
||||||
self.buf = ""
|
self.buf = ""
|
||||||
|
|
||||||
def recv(self, buffer_size):
|
def recv(self, buffer_size):
|
||||||
data = self.sock.recv(buffer_size)
|
try:
|
||||||
if not data:
|
return self.sock.recv(buffer_size)
|
||||||
# we should handle close here
|
except socket.error, e:
|
||||||
return ''
|
if e[0] == EWOULDBLOCK:
|
||||||
return data
|
return None
|
||||||
|
if e[0] in SOCKET_CLOSED:
|
||||||
|
return ''
|
||||||
|
raise
|
||||||
|
|
||||||
def send(self, data):
|
def send(self, data):
|
||||||
return self.sock.send(data)
|
return self.sock.send(data)
|
||||||
|
|||||||
@ -20,9 +20,11 @@ import StringIO
|
|||||||
import sys
|
import sys
|
||||||
from urllib import unquote
|
from urllib import unquote
|
||||||
|
|
||||||
|
|
||||||
from gunicorn import __version__
|
from gunicorn import __version__
|
||||||
from gunicorn.http.iostream import IOStream
|
from gunicorn.http.iostream import IOStream
|
||||||
from gunicorn.util import http_date
|
|
||||||
|
|
||||||
|
|
||||||
NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+')
|
NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+')
|
||||||
|
|
||||||
@ -54,6 +56,7 @@ class HTTPRequest(object):
|
|||||||
self._version = 11
|
self._version = 11
|
||||||
self.io = IOStream(socket)
|
self.io = IOStream(socket)
|
||||||
self.start_response_called = False
|
self.start_response_called = False
|
||||||
|
self._should_close = False
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
# read headers
|
# read headers
|
||||||
@ -130,6 +133,8 @@ class HTTPRequest(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def should_close(self):
|
def should_close(self):
|
||||||
|
if self._should_close:
|
||||||
|
return True
|
||||||
if self.headers.get("CONNECTION") == "close":
|
if self.headers.get("CONNECTION") == "close":
|
||||||
return True
|
return True
|
||||||
if self.headers.get("CONNECTION") == "Keep-Alive":
|
if self.headers.get("CONNECTION") == "Keep-Alive":
|
||||||
@ -159,30 +164,22 @@ class HTTPRequest(object):
|
|||||||
data.seek(0)
|
data.seek(0)
|
||||||
return data, str(length) or ""
|
return data, str(length) or ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def start_response(self, status, response_headers):
|
def start_response(self, status, response_headers):
|
||||||
self.start_response_called = True
|
|
||||||
resp_head = []
|
|
||||||
self.response_status = int(status.split(" ")[0])
|
|
||||||
self.response_headers = {}
|
|
||||||
resp_head.append("%s %ss\r\n" % (self.version, status))
|
|
||||||
|
|
||||||
resp_head.append("Server: %s\r\n" % self.SERVER_VERSION)
|
self.response_status = int(status.split(" ")[0])
|
||||||
resp_head.append("Date: %s\r\n" % http_date())
|
|
||||||
# broken clients
|
|
||||||
resp_head.append("Status: %s\r\n" % str(self.response_status))
|
|
||||||
for name, value in response_headers:
|
for name, value in response_headers:
|
||||||
resp_head.append("%s: %s\r\n" % (_normalize_name(name), value.strip()))
|
name = _normalize_name(name)
|
||||||
self.response_headers[name.lower()] = value
|
self.response_headers[name] = value.strip()
|
||||||
self.io.send("%s\r\n" % "".join(resp_head))
|
|
||||||
|
self.start_response_called = True
|
||||||
|
print "response called"
|
||||||
|
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
self.io.write(send)
|
self.io.write(send)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self.should_close():
|
self.socket.close()
|
||||||
self.socket.close()
|
|
||||||
|
|
||||||
def first_line(self, line):
|
def first_line(self, line):
|
||||||
method, path, version = line.strip().split(" ")
|
method, path, version = line.strip().split(" ")
|
||||||
@ -195,7 +192,7 @@ class HTTPRequest(object):
|
|||||||
name = name.strip().upper()
|
name = name.strip().upper()
|
||||||
self.headers[name] = value.strip()
|
self.headers[name] = value.strip()
|
||||||
return name
|
return name
|
||||||
|
|
||||||
class FileInput(object):
|
class FileInput(object):
|
||||||
|
|
||||||
stream_size = 4096
|
stream_size = 4096
|
||||||
@ -217,13 +214,14 @@ class FileInput(object):
|
|||||||
s = self._rbuf[:amt]
|
s = self._rbuf[:amt]
|
||||||
self._rbuf = self._rbuf[amt:]
|
self._rbuf = self._rbuf[amt:]
|
||||||
return s
|
return s
|
||||||
|
|
||||||
data = self.io.recv(amt)
|
data = self.io.recv(amt)
|
||||||
s = self._rbuf + data
|
s = self._rbuf + data
|
||||||
self._rbuf = ''
|
self._rbuf = ''
|
||||||
|
print "return %s" % s
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def readline(self, amt=-1):
|
def readline(self, amt=-1):
|
||||||
print "ici"
|
|
||||||
i = self._rbuf.find('\n')
|
i = self._rbuf.find('\n')
|
||||||
while i < 0 and not (0 < amt <= len(self._rbuf)):
|
while i < 0 and not (0 < amt <= len(self._rbuf)):
|
||||||
new = self.io.recv(self.stream_size)
|
new = self.io.recv(self.stream_size)
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
from gunicorn.util import http_date
|
||||||
|
|
||||||
class HTTPResponse(object):
|
class HTTPResponse(object):
|
||||||
|
|
||||||
@ -29,9 +29,23 @@ class HTTPResponse(object):
|
|||||||
self.io.send(data)
|
self.io.send(data)
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
if self.req.method == "HEAD":
|
# send headers
|
||||||
return
|
resp_head = []
|
||||||
|
resp_head.append("%s %ss\r\n" % (self.req.version, self.req.response_status))
|
||||||
|
|
||||||
|
resp_head.append("Server: %s\r\n" % self.req.SERVER_VERSION)
|
||||||
|
resp_head.append("Date: %s\r\n" % http_date())
|
||||||
|
# broken clients
|
||||||
|
resp_head.append("Status: %s\r\n" % str(self.req.response_status))
|
||||||
|
for name, value in self.req.response_headers.items():
|
||||||
|
resp_head.append("%s: %s\r\n" % (name, value))
|
||||||
|
self.io.send("%s\r\n" % "".join(resp_head))
|
||||||
|
|
||||||
|
|
||||||
for chunk in self.data:
|
for chunk in self.data:
|
||||||
self.write(chunk)
|
self.write(chunk)
|
||||||
self.data.close()
|
self.req.close()
|
||||||
|
|
||||||
|
if hasattr(self.data, "close"):
|
||||||
|
self.data.close()
|
||||||
|
|
||||||
@ -16,6 +16,7 @@
|
|||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
|
import fcntl
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import select
|
import select
|
||||||
@ -82,13 +83,11 @@ class Worker(object):
|
|||||||
break # Jump back to select
|
break # Jump back to select
|
||||||
raise # Uh oh!
|
raise # Uh oh!
|
||||||
|
|
||||||
conn.setblocking(1)
|
conn.setblocking(0)
|
||||||
try:
|
try:
|
||||||
self.handle(conn, addr)
|
self.handle(conn, addr)
|
||||||
except:
|
except:
|
||||||
log.exception("Error processing request.")
|
log.exception("Error processing request.")
|
||||||
finally:
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
# Update the fd mtime on each client completion
|
# Update the fd mtime on each client completion
|
||||||
# to signal that this worker process is alive.
|
# to signal that this worker process is alive.
|
||||||
@ -96,11 +95,8 @@ class Worker(object):
|
|||||||
os.fchmod(self.tmp.fileno(), spinner)
|
os.fchmod(self.tmp.fileno(), spinner)
|
||||||
|
|
||||||
def handle(self, conn, client):
|
def handle(self, conn, client):
|
||||||
while True:
|
fcntl.fcntl(conn.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
|
||||||
req = http.HTTPRequest(conn, client, self.address)
|
req = http.HTTPRequest(conn, client, self.address)
|
||||||
result = self.app(req.read(), req.start_response)
|
result = self.app(req.read(), req.start_response)
|
||||||
response = http.HTTPResponse(req, result)
|
response = http.HTTPResponse(req, result)
|
||||||
response.send()
|
response.send()
|
||||||
if req.should_close():
|
|
||||||
conn.close()
|
|
||||||
return
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user