diff --git a/gunicorn/arbiter.py b/gunicorn/arbiter.py index 9cbfdb87..e4076c71 100644 --- a/gunicorn/arbiter.py +++ b/gunicorn/arbiter.py @@ -34,7 +34,7 @@ import socket import sys import time -from .worker import Worker +from gunicorn.worker import Worker log = logging.getLogger(__name__) @@ -122,17 +122,18 @@ class Arbiter(object): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.set_sockopts(sock) sock.bind(address) - sock.listen(2048) + sock.listen(1024) return sock - def set_sockopts(self, sock): + def set_sockopts(self, sock): sock.setblocking(0) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 0) + 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) + def run(self): self.manage_workers() diff --git a/gunicorn/http/__init__.py b/gunicorn/http/__init__.py index 28002a0c..ebe3dccf 100644 --- a/gunicorn/http/__init__.py +++ b/gunicorn/http/__init__.py @@ -24,5 +24,5 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. -from request import HTTPRequest, RequestError -from response import HTTPResponse \ No newline at end of file +from gunicorn.http.request import HTTPRequest, RequestError +from gunicorn.http.response import HTTPResponse \ No newline at end of file diff --git a/gunicorn/http/http_parser.py b/gunicorn/http/http_parser.py index bf83539f..0eea92d6 100644 --- a/gunicorn/http/http_parser.py +++ b/gunicorn/http/http_parser.py @@ -22,7 +22,6 @@ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. -from ctypes import * class HttpParser(object): @@ -35,7 +34,8 @@ class HttpParser(object): def headers(self, headers, buf): """ take a string buff. It return - environ or None if parsing isn't done. + new position or -1 if parsing isn't done. + headers dict is updated. """ if self._headers: return self._headers @@ -53,6 +53,7 @@ class HttpParser(object): return -1 def finalize_headers(self, headers, headers_str, pos): + """ parse the headers """ lines = headers_str.split("\r\n") # parse first line of headers @@ -75,12 +76,14 @@ class HttpParser(object): return pos def _first_line(self, line): + """ parse first line """ method, path, version = line.strip().split(" ") self.version = version.strip() self.method = method.upper() self.path = path def _parse_headerl(self, line): + """ parse header line""" name, value = line.split(": ", 1) name = name.strip() self._headers[name] = value.strip() @@ -99,11 +102,14 @@ class HttpParser(object): @property def is_chunked(self): + """ is TE: chunked ?""" transfert_encoding = self._headers.get('Transfer-Encoding', False) return (transfert_encoding == "chunked") @property def content_len(self): + """ return content length as integer or + None.""" transfert_encoding = self._headers.get('Transfer-Encoding') content_length = self._headers.get('Content-Length') if transfert_encoding is None: @@ -114,6 +120,7 @@ class HttpParser(object): return None def body_eof(self): + """do we have all the body ?""" #TODO : add chunk if self._content_len == 0: return True @@ -122,7 +129,7 @@ class HttpParser(object): def read_chunk(self, data): dlen = len(data) i = data.find("\n") - if i != -1: + if i != -1: chunk = data[:i].strip().split(";", 1) chunk_size = int(line.pop(0), 16) if chunk_size <= 0: @@ -131,6 +138,11 @@ class HttpParser(object): self.start_offset = i+1 def filter_body(self, data): + """ filter body and return a tuple: + body_chunk, new_buffer. They could be None. + new_fubber is always None if it's empty. + + """ dlen = len(data) chunk = None if self.is_chunked: @@ -138,10 +150,9 @@ class HttpParser(object): else: if self._content_len > 0: nr = min(dlen, self._content_len) - print nr chunk = data[:nr] self._content_len -= nr data = None self.start_offset = 0 - return chunk, data \ No newline at end of file + return (chunk, data) \ No newline at end of file diff --git a/gunicorn/http/request.py b/gunicorn/http/request.py index 977dc1fd..853415dd 100644 --- a/gunicorn/http/request.py +++ b/gunicorn/http/request.py @@ -35,9 +35,9 @@ import array import logging from gunicorn import __version__ -from .http_parser import HttpParser -from .tee import TeeInput -from ..util import CHUNK_SIZE, read_partial +from gunicorn.http.http_parser import HttpParser +from gunicorn.http.tee import TeeInput +from gunicorn.util import CHUNK_SIZE, read_partial NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+') @@ -80,8 +80,7 @@ class HTTPRequest(object): buf += data i = self.parser.headers(headers, buf) if i != -1: break - if not buf: - self.socket.close() + if not headers: return {} diff --git a/gunicorn/http/response.py b/gunicorn/http/response.py index 81b29c2a..8464599d 100644 --- a/gunicorn/http/response.py +++ b/gunicorn/http/response.py @@ -26,15 +26,17 @@ import errno import socket +import select import time -from ..util import http_date, write, read_partial +import os +from gunicorn.util import http_date, write, read_partial, close class HTTPResponse(object): def __init__(self, sock, response, req): self.req = req - self.sock = sock + self.sock = sock.dup() self.data = response self.headers = req.response_headers or {} self.status = req.response_status @@ -58,8 +60,9 @@ class HTTPResponse(object): for chunk in self.data: write(self.sock, chunk) - - self.sock.close() + + + close(self.sock) if hasattr(self.data, "close"): self.data.close() \ No newline at end of file diff --git a/gunicorn/http/tee.py b/gunicorn/http/tee.py index c7f94e6c..39e3db06 100644 --- a/gunicorn/http/tee.py +++ b/gunicorn/http/tee.py @@ -34,9 +34,8 @@ read or restart etc ... It's based on TeeInput from unicorn. import os import StringIO import tempfile -from ctypes import create_string_buffer -from ..util import MAX_BODY, CHUNK_SIZE +from gunicorn.util import MAX_BODY, CHUNK_SIZE class TeeInput(object): @@ -121,8 +120,7 @@ class TeeInput(object): break line = self.readline() return lines - - + def next(self): r = self.readline() if not r: diff --git a/gunicorn/main.py b/gunicorn/main.py index a7cad1d6..714d727a 100644 --- a/gunicorn/main.py +++ b/gunicorn/main.py @@ -29,7 +29,7 @@ import optparse as op import os import sys -from arbiter import Arbiter +from gunicorn.arbiter import Arbiter LOG_LEVELS = { "critical": logging.CRITICAL, diff --git a/gunicorn/util.py b/gunicorn/util.py index 556824c9..0eb9580d 100644 --- a/gunicorn/util.py +++ b/gunicorn/util.py @@ -42,12 +42,22 @@ monthname = [None, 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] +def close(sock): + try: + sock.shutdown(2) + except socket.error: + pass + try: + sock.close() + except socket.error: + pass + def read_partial(sock, length): while True: try: ret = select.select([sock.fileno()], [], [], 2.0) if ret[0]: break - except socket.error, e: + except select.error, e: if e[0] == errno.EINTR: break raise @@ -70,7 +80,6 @@ def write_nonblock(sock, data): if ret[1]: break except socket.error, e: if e[0] == errno.EINTR: - time.sleep(1) break raise write(sock, data) diff --git a/gunicorn/worker.py b/gunicorn/worker.py index bb85e7e2..367d29c9 100644 --- a/gunicorn/worker.py +++ b/gunicorn/worker.py @@ -35,8 +35,8 @@ import sys import tempfile import time -from . import http -from . import util +from gunicorn import http +from gunicorn import util class Worker(object): @@ -96,7 +96,7 @@ class Worker(object): while self.alive: try: - ret = select.select([self.socket], [], [], 2.0) + ret = select.select([self.socket], [], [], 15) if ret[0]: break except select.error, e: @@ -115,11 +115,7 @@ class Worker(object): # loop and wait for some lovin. while self.alive: try: - res = self.socket.accept() - if res is None: - break - client, addr = res - client.setblocking(0) + client, addr = self.socket.accept() # handle connection self.handle(client, addr) @@ -143,5 +139,7 @@ class Worker(object): except Exception, e: self.log.exception("Error processing request. [%s]" % str(e)) msg = "HTTP/1.0 500 Internal Server Error\r\n\r\n" - util.write_nonblock(client, msg) - client.close() + #util.write_nonblock(client, msg) + util.close(client) + + del client