diff --git a/gunicorn/arbiter.py b/gunicorn/arbiter.py index 860b5033..750a724f 100644 --- a/gunicorn/arbiter.py +++ b/gunicorn/arbiter.py @@ -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 diff --git a/gunicorn/http/iostream.py b/gunicorn/http/iostream.py index 64814138..23d7af1f 100644 --- a/gunicorn/http/iostream.py +++ b/gunicorn/http/iostream.py @@ -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) diff --git a/gunicorn/http/request.py b/gunicorn/http/request.py index c22ae64e..7ef7514c 100644 --- a/gunicorn/http/request.py +++ b/gunicorn/http/request.py @@ -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) diff --git a/gunicorn/http/response.py b/gunicorn/http/response.py index cb71e249..ba4235d4 100644 --- a/gunicorn/http/response.py +++ b/gunicorn/http/response.py @@ -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() \ No newline at end of file diff --git a/gunicorn/worker.py b/gunicorn/worker.py index f66a8e81..f584eb47 100644 --- a/gunicorn/worker.py +++ b/gunicorn/worker.py @@ -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()