naive working integration of simplehttp parser. async worker is slower

than the non async actually for unkown reason and slower than with old
parser.
This commit is contained in:
benoitc 2010-04-27 15:14:25 +02:00 committed by Paul J. Davis
parent eb0d54e111
commit d9a2579613
5 changed files with 62 additions and 64 deletions

View File

@ -17,6 +17,8 @@ except ImportError:
import sys
from urllib import unquote
from simplehttp import RequestParser
from gunicorn import __version__
from gunicorn.http.parser import Parser
from gunicorn.http.response import Response, KeepAliveResponse
@ -54,41 +56,54 @@ class Request(object):
self.response_status = None
self.response_headers = []
self._version = 11
self.parser = Parser.parse_request()
self.parser = RequestParser(self.socket)
self.log = logging.getLogger(__name__)
self.response = None
self.response_chunked = False
self.headers_sent = False
self.req = None
def read(self):
environ = {}
headers = []
buf = StringIO()
data = self.socket.recv(CHUNK_SIZE)
buf.write(data)
buf2 = self.parser.filter_headers(headers, buf)
if not buf2:
while True:
data = self.socket.recv(CHUNK_SIZE)
if not data:
break
buf.write(data)
buf2 = self.parser.filter_headers(headers, buf)
if buf2:
break
self.log.debug("%s", self.parser.status)
self.log.debug("Headers:\n%s" % headers)
if self.parser.headers_dict.get('Expect','').lower() == "100-continue":
self.socket.send("HTTP/1.1 100 Continue\r\n\r\n")
if not self.parser.content_len and not self.parser.is_chunked:
wsgi_input = TeeInput(self.cfg, self.socket, self.parser, StringIO())
content_length = "0"
ended = False
req = None
self.req = req = self.parser.next()
##self.log.debug("%s", self.parser.status)
self.log.debug("Headers:\n%s" % req.headers)
# authors should be aware that REMOTE_HOST and REMOTE_ADDR
# may not qualify the remote addr:
# http://www.ietf.org/rfc/rfc3875
client_address = self.client_address or "127.0.0.1"
forward_address = client_address
server_address = self.server_address
script_name = os.environ.get("SCRIPT_NAME", "")
content_type = ""
for hdr_name, hdr_value in req.headers:
name = hdr_name.lower()
if name == "expect":
# handle expect
if hdr_value.lower() == "100-continue":
self.socket.send("HTTP/1.1 100 Continue\r\n\r\n")
elif name == "x-forwarded-for":
forward_address = hdr_value
elif name == "host":
host = hdr_value
elif name == "script_name":
script_name = hdr_value
elif name == "content-type":
content_type = hdr_value
wsgi_input = req.body
if hasattr(req.body, "length"):
content_length = str(req.body.length)
else:
wsgi_input = TeeInput(self.cfg, self.socket, self.parser, buf2)
content_length = str(wsgi_input.len)
content_length = None
# This value should evaluate true if an equivalent application
# object may be simultaneously invoked by another process, and
@ -96,44 +111,27 @@ class Request(object):
# worker so we comply to pylons and other paster app.
wsgi_multiprocess = self.cfg.workers > 1
# authors should be aware that REMOTE_HOST and REMOTE_ADDR
# may not qualify the remote addr:
# http://www.ietf.org/rfc/rfc3875
client_address = self.client_address or "127.0.0.1"
forward_adress = self.parser.headers_dict.get('X-Forwarded-For',
client_address)
if self.parser.headers_dict.get("X-Forwarded-Protocol") == "https" or \
self.parser.headers_dict.get("X-Forwarded-Ssl") == "on":
url_scheme = "https"
else:
url_scheme = "http"
if isinstance(forward_adress, basestring):
if isinstance(forward_address, basestring):
# we only took the last one
# http://en.wikipedia.org/wiki/X-Forwarded-For
if "," in forward_adress:
forward_adress = forward_adress.split(",")[-1].strip()
remote_addr = forward_adress.split(":")
if "," in forward_address:
forward_adress = forward_address.split(",")[-1].strip()
remote_addr = forward_address.split(":")
if len(remote_addr) == 1:
remote_addr.append('')
else:
remote_addr = forward_adress
# Try to server address from headers
server_address = self.parser.headers_dict.get('Host',
self.server_address)
remote_addr = forward_address
if isinstance(server_address, basestring):
server_address = server_address.split(":")
if len(server_address) == 1:
server_address.append('')
script_name = self.parser.headers_dict.get("SCRIPT_NAME",
os.environ.get("SCRIPT_NAME", ""))
path_info = self.parser.path
path_info = req.path
if script_name:
path_info = path_info.split(script_name, 1)[-1]
environ = {
"wsgi.url_scheme": url_scheme,
"wsgi.input": wsgi_input,
@ -144,23 +142,24 @@ class Request(object):
"wsgi.run_once": False,
"SCRIPT_NAME": script_name,
"SERVER_SOFTWARE": self.SERVER_VERSION,
"REQUEST_METHOD": self.parser.method,
"REQUEST_METHOD": req.method,
"PATH_INFO": unquote(path_info),
"QUERY_STRING": self.parser.query_string,
"RAW_URI": self.parser.raw_path,
"CONTENT_TYPE": self.parser.headers_dict.get('Content-Type', ''),
"QUERY_STRING": req.query,
"RAW_URI": req.path,
"CONTENT_TYPE": content_type,
"CONTENT_LENGTH": content_length,
"REMOTE_ADDR": remote_addr[0],
"REMOTE_PORT": str(remote_addr[1]),
"SERVER_NAME": server_address[0],
"SERVER_PORT": str(server_address[1]),
"SERVER_PROTOCOL": self.parser.raw_version
"SERVER_PROTOCOL": req.version
}
for key, value in self.parser.headers:
for key, value in req.headers:
key = 'HTTP_' + key.upper().replace('-', '_')
if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
environ[key] = value
return environ
def start_response(self, status, headers, exc_info=None):

View File

@ -62,7 +62,7 @@ class KeepAliveResponse(Response):
def default_headers(self):
connection = "keep-alive"
if self.req.parser.should_close:
if self.req.req.should_close():
connection = "close"
return [

View File

@ -55,7 +55,7 @@ class AsyncWorker(Worker):
return False
try:
environ = req.read()
if not environ or not req.parser.headers:
if not environ:
return False
respiter = self.wsgi(environ, req.start_response)
if respiter == ALREADY_HANDLED:
@ -65,7 +65,7 @@ class AsyncWorker(Worker):
req.response.close()
if hasattr(respiter, "close"):
respiter.close()
if req.parser.should_close:
if req.req.should_close():
return False
except Exception, e:
#Only send back traceback in HTTP in debug mode.

View File

@ -92,7 +92,7 @@ class SyncWorker(Worker):
req = http.Request(self.cfg, client, addr, self.address)
try:
environ = req.read()
if not environ or not req.parser.status_line:
if not environ:
return
respiter = self.wsgi(environ, req.start_response)
for item in respiter:

View File

@ -41,9 +41,8 @@ setup(
packages = find_packages(exclude=['examples', 'tests']),
include_package_data = True,
install_requires=['setuptools'],
install_requires=['setuptools', 'simplehttp'],
entry_points="""
[console_scripts]