lot of fixes. nonblocking socket need to be handled correctly though.

This commit is contained in:
Benoit Chesneau 2009-12-22 02:48:30 +01:00
parent 7fccf32c06
commit cb79c8514d
5 changed files with 59 additions and 40 deletions

View File

@ -99,6 +99,11 @@ class Arbiter(object):
sock.setblocking(0)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 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.listen(2048)
return sock

View File

@ -22,20 +22,26 @@ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, \
import socket
SOCKET_CLOSED = (ECONNRESET, ENOTCONN, ESHUTDOWN)
class IOStream(object):
chunk_size = 4096
def __init__(self, sock):
self.sock = sock
self.sock = sock.dup()
self.sock.setblocking(0)
self.buf = ""
def recv(self, buffer_size):
data = self.sock.recv(buffer_size)
if not data:
# we should handle close here
return ''
return data
try:
return self.sock.recv(buffer_size)
except socket.error, e:
if e[0] == EWOULDBLOCK:
return None
if e[0] in SOCKET_CLOSED:
return ''
raise
def send(self, data):
return self.sock.send(data)

View File

@ -20,9 +20,11 @@ import StringIO
import sys
from urllib import unquote
from gunicorn import __version__
from gunicorn.http.iostream import IOStream
from gunicorn.util import http_date
NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+')
@ -54,6 +56,7 @@ class HTTPRequest(object):
self._version = 11
self.io = IOStream(socket)
self.start_response_called = False
self._should_close = False
def read(self):
# read headers
@ -130,6 +133,8 @@ class HTTPRequest(object):
return None
def should_close(self):
if self._should_close:
return True
if self.headers.get("CONNECTION") == "close":
return True
if self.headers.get("CONNECTION") == "Keep-Alive":
@ -159,30 +164,22 @@ class HTTPRequest(object):
data.seek(0)
return data, str(length) or ""
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)
resp_head.append("Date: %s\r\n" % http_date())
# broken clients
resp_head.append("Status: %s\r\n" % str(self.response_status))
self.response_status = int(status.split(" ")[0])
for name, value in response_headers:
resp_head.append("%s: %s\r\n" % (_normalize_name(name), value.strip()))
self.response_headers[name.lower()] = value
self.io.send("%s\r\n" % "".join(resp_head))
name = _normalize_name(name)
self.response_headers[name] = value.strip()
self.start_response_called = True
print "response called"
def write(self, data):
self.io.write(send)
def close(self):
if self.should_close():
self.socket.close()
self.socket.close()
def first_line(self, line):
method, path, version = line.strip().split(" ")
@ -195,7 +192,7 @@ class HTTPRequest(object):
name = name.strip().upper()
self.headers[name] = value.strip()
return name
class FileInput(object):
stream_size = 4096
@ -217,13 +214,14 @@ class FileInput(object):
s = self._rbuf[:amt]
self._rbuf = self._rbuf[amt:]
return s
data = self.io.recv(amt)
s = self._rbuf + data
self._rbuf = ''
print "return %s" % s
return s
def readline(self, amt=-1):
print "ici"
i = self._rbuf.find('\n')
while i < 0 and not (0 < amt <= len(self._rbuf)):
new = self.io.recv(self.stream_size)

View File

@ -15,7 +15,7 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from gunicorn.util import http_date
class HTTPResponse(object):
@ -29,9 +29,23 @@ class HTTPResponse(object):
self.io.send(data)
def send(self):
if self.req.method == "HEAD":
return
# send headers
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:
self.write(chunk)
self.data.close()
self.req.close()
if hasattr(self.data, "close"):
self.data.close()

View File

@ -16,6 +16,7 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import errno
import fcntl
import logging
import os
import select
@ -82,13 +83,11 @@ class Worker(object):
break # Jump back to select
raise # Uh oh!
conn.setblocking(1)
conn.setblocking(0)
try:
self.handle(conn, addr)
except:
log.exception("Error processing request.")
finally:
conn.close()
# Update the fd mtime on each client completion
# to signal that this worker process is alive.
@ -96,11 +95,8 @@ class Worker(object):
os.fchmod(self.tmp.fileno(), spinner)
def handle(self, conn, client):
while True:
req = http.HTTPRequest(conn, client, self.address)
result = self.app(req.read(), req.start_response)
response = http.HTTPResponse(req, result)
response.send()
if req.should_close():
conn.close()
return
fcntl.fcntl(conn.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
req = http.HTTPRequest(conn, client, self.address)
result = self.app(req.read(), req.start_response)
response = http.HTTPResponse(req, result)
response.send()