mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
no buffer read. Instead of using socket fileobject , read it directly
using our own stream class
This commit is contained in:
parent
89bae0daf2
commit
753f3bcf1a
76
gunicorn/http/iostream.py
Normal file
76
gunicorn/http/iostream.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2008,2009 Benoit Chesneau <benoitc@e-engura.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at#
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, \
|
||||||
|
ENOTCONN, ESHUTDOWN, EINTR, EISCONN, EBADF, ECONNABORTED, errorcode
|
||||||
|
|
||||||
|
import socket
|
||||||
|
|
||||||
|
class IOStream(object):
|
||||||
|
|
||||||
|
chunk_size = 4096
|
||||||
|
|
||||||
|
def __init__(self, sock):
|
||||||
|
self.sock = sock
|
||||||
|
|
||||||
|
self.buf = ""
|
||||||
|
|
||||||
|
|
||||||
|
def recv(self, buffer_size):
|
||||||
|
try:
|
||||||
|
data = self.sock.recv(buffer_size)
|
||||||
|
if not data:
|
||||||
|
# we should handle close here
|
||||||
|
return ''
|
||||||
|
return data
|
||||||
|
except socket.error, e:
|
||||||
|
if e.args[0] in (errno.ECONNRESET, errno.ENOTCONN,
|
||||||
|
errno.ESHUTDOWN, errno.ECONNABORTED):
|
||||||
|
# we should handle close here
|
||||||
|
return ''
|
||||||
|
raise
|
||||||
|
|
||||||
|
def send(self, data):
|
||||||
|
try:
|
||||||
|
rst = self.sock.send(data)
|
||||||
|
return rst
|
||||||
|
except socket.error, e:
|
||||||
|
if e.args[0] == EWOULDBLOCK:
|
||||||
|
return 0
|
||||||
|
elif e.args[0] in (errno.ECONNRESET, errno.ENOTCONN,
|
||||||
|
errno.ESHUTDOWN, errno.ECONNABORTED):
|
||||||
|
# we should handle close here
|
||||||
|
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def read_until(self, delimiter):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data = self.recv(self.chunk_size)
|
||||||
|
except socket.error, e:
|
||||||
|
return
|
||||||
|
self.buf = self.buf + data
|
||||||
|
|
||||||
|
lb = len(self.buf)
|
||||||
|
ld = len(delimiter)
|
||||||
|
i = self.buf.find(delimiter)
|
||||||
|
if i != -1:
|
||||||
|
if i > 0:
|
||||||
|
r = self.buf[:i]
|
||||||
|
self.buf = self.buf[i+ ld:]
|
||||||
|
return r
|
||||||
@ -20,6 +20,7 @@ import sys
|
|||||||
from urllib import unquote
|
from urllib import unquote
|
||||||
|
|
||||||
from gunicorn import __version__
|
from gunicorn import __version__
|
||||||
|
from gunicorn.http.iostream import IOStream
|
||||||
|
|
||||||
NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+')
|
NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+')
|
||||||
|
|
||||||
@ -49,15 +50,13 @@ class HTTPRequest(object):
|
|||||||
self.response_status = None
|
self.response_status = None
|
||||||
self.response_headers = {}
|
self.response_headers = {}
|
||||||
self._version = 11
|
self._version = 11
|
||||||
self.fp = socket.makefile("rw", self.CHUNK_SIZE)
|
self.io = IOStream(socket)
|
||||||
|
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
# get status line
|
|
||||||
self.first_line(self.fp.readline())
|
|
||||||
|
|
||||||
# read headers
|
# read headers
|
||||||
self.read_headers()
|
self.read_headers(first_line=True)
|
||||||
|
|
||||||
if "?" in self.path:
|
if "?" in self.path:
|
||||||
path_info, query = self.path.split('?', 1)
|
path_info, query = self.path.split('?', 1)
|
||||||
@ -103,17 +102,13 @@ class HTTPRequest(object):
|
|||||||
environ[key] = value
|
environ[key] = value
|
||||||
return environ
|
return environ
|
||||||
|
|
||||||
def read_headers(self):
|
def read_headers(self, first_line=False):
|
||||||
|
headers_str = self.io.read_until("\r\n\r\n")
|
||||||
|
lines = headers_str.split("\r\n")
|
||||||
|
self.first_line(lines.pop(0))
|
||||||
hname = ""
|
hname = ""
|
||||||
while True:
|
for line in lines:
|
||||||
line = self.fp.readline()
|
|
||||||
|
|
||||||
if line == "\r\n":
|
|
||||||
# end of headers
|
|
||||||
break
|
|
||||||
|
|
||||||
if line == "\t":
|
if line == "\t":
|
||||||
# It's a continuation line.
|
|
||||||
self.headers[hname] += line.strip()
|
self.headers[hname] += line.strip()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@ -147,13 +142,13 @@ class HTTPRequest(object):
|
|||||||
length = 0
|
length = 0
|
||||||
data = StringIO.StringIO()
|
data = StringIO.StringIO()
|
||||||
while True:
|
while True:
|
||||||
line = self.fp.readline().strip().split(";", 1)
|
line = self.io.readuntil("\n").strip().split(";", 1)
|
||||||
chunk_size = int(line.pop(0), 16)
|
chunk_size = int(line.pop(0), 16)
|
||||||
if chunk_size <= 0:
|
if chunk_size <= 0:
|
||||||
break
|
break
|
||||||
length += chunk_size
|
length += chunk_size
|
||||||
data.write(self.fp.read(chunk_size))
|
data.write(self.io.recv(chunk_size))
|
||||||
crlf = self.fp.read(2)
|
crlf = self.io.read(2)
|
||||||
if crlf != "\r\n":
|
if crlf != "\r\n":
|
||||||
raise RequestError((400, "Bad chunked transfer coding "
|
raise RequestError((400, "Bad chunked transfer coding "
|
||||||
"(expected '\\r\\n', got %r)" % crlf))
|
"(expected '\\r\\n', got %r)" % crlf))
|
||||||
@ -173,13 +168,12 @@ class HTTPRequest(object):
|
|||||||
for name, value in response_headers:
|
for name, value in response_headers:
|
||||||
resp_head.append("%s: %s" % (name, value))
|
resp_head.append("%s: %s" % (name, value))
|
||||||
self.response_headers[name.lower()] = value
|
self.response_headers[name.lower()] = value
|
||||||
self.fp.write("%s\r\n\r\n" % "\r\n".join(resp_head))
|
self.io.send("%s\r\n\r\n" % "\r\n".join(resp_head))
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
self.fp.write(data)
|
self.io.write(send)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.fp.close()
|
|
||||||
if self.should_close():
|
if self.should_close():
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
|
|
||||||
@ -199,57 +193,49 @@ class HTTPRequest(object):
|
|||||||
|
|
||||||
class FileInput(object):
|
class FileInput(object):
|
||||||
|
|
||||||
|
stream_size = 4096
|
||||||
|
|
||||||
def __init__(self, req):
|
def __init__(self, req):
|
||||||
self.length = req.body_length()
|
self.length = req.body_length()
|
||||||
self.fp = req.fp
|
self.io = req.io
|
||||||
self.eof = False
|
self._rbuf = ""
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.eof = False
|
self.eof = False
|
||||||
|
|
||||||
def read(self, amt=None):
|
def read(self, amt=None):
|
||||||
if self.fp is None or self.eof:
|
if self._rbuf and not amt is None:
|
||||||
return ''
|
L = len(self._rbuf)
|
||||||
|
if amt > L:
|
||||||
if amt is None:
|
amt -= L
|
||||||
# unbounded read
|
else:
|
||||||
s = self._safe_read(self.length)
|
s = self._rbuf[:amt]
|
||||||
self.close() # we read everything
|
self._rbuf = self._rbuf[amt:]
|
||||||
return s
|
return s
|
||||||
|
data = self.io.recv(amt)
|
||||||
if amt > self.length:
|
s = self._rbuf + data
|
||||||
amt = self.length
|
self._rbuf = ''
|
||||||
|
|
||||||
s = self.fp.read(amt)
|
|
||||||
self.length -= len(s)
|
|
||||||
if not self.length:
|
|
||||||
self.close()
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def readline(self, size=None):
|
def readline(self, amt=-1):
|
||||||
if self.fp is None or self.eof:
|
i = self._rbuf.find('\n')
|
||||||
return ''
|
while i < 0 and not (0 < amt <= len(self._rbuf)):
|
||||||
|
new = self.io.recv(self.stream_size)
|
||||||
if size is not None:
|
if not new: break
|
||||||
data = self.fp.readline(size)
|
i = new.find('\n')
|
||||||
else:
|
if i >= 0:
|
||||||
# User didn't specify a size ...
|
i = i + len(self._rbuf)
|
||||||
# We read the line in chunks to make sure it's not a 100MB line !
|
self._rbuf = self._rbuf + new
|
||||||
# cherrypy trick
|
if i < 0:
|
||||||
res = []
|
i = len(self._rbuf)
|
||||||
while True:
|
else:
|
||||||
data = self.fp.readline(256)
|
i = i+1
|
||||||
res.append(data)
|
if 0 <= amt < len(self._rbuf):
|
||||||
if len(data) < 256 or data[-1:] == "\n":
|
i = amt
|
||||||
data = ''.join(res)
|
data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
|
||||||
break
|
|
||||||
self.length -= len(data)
|
|
||||||
if not self.length:
|
|
||||||
self.close()
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def readlines(self, sizehint=0):
|
def readlines(self, sizehint=0):
|
||||||
# Shamelessly stolen from StringIO
|
|
||||||
total = 0
|
total = 0
|
||||||
lines = []
|
lines = []
|
||||||
line = self.readline()
|
line = self.readline()
|
||||||
@ -261,27 +247,14 @@ class FileInput(object):
|
|||||||
line = self.readline()
|
line = self.readline()
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
r = self.readline()
|
||||||
|
if not r:
|
||||||
|
raise StopIteration
|
||||||
|
return r
|
||||||
|
|
||||||
def _safe_read(self, amt):
|
|
||||||
"""Read the number of bytes requested, compensating for partial reads.
|
|
||||||
"""
|
|
||||||
s = []
|
|
||||||
while amt > 0:
|
|
||||||
chunk = self.fp.read(amt)
|
|
||||||
if not chunk:
|
|
||||||
raise RequestError(500, "Incomplete read %s" % s)
|
|
||||||
s.append(chunk)
|
|
||||||
amt -= len(chunk)
|
|
||||||
return ''.join(s)
|
|
||||||
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def next(self):
|
|
||||||
if self.eof:
|
|
||||||
raise StopIteration()
|
|
||||||
return self.readline()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -21,13 +21,12 @@ class HTTPResponse(object):
|
|||||||
self.req = req
|
self.req = req
|
||||||
self.data = data
|
self.data = data
|
||||||
self.headers = self.req.response_headers or {}
|
self.headers = self.req.response_headers or {}
|
||||||
self.fp = req.fp
|
self.io = req.io
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
self.fp.write(data)
|
self.io.send(data)
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
if not self.data: return
|
if not self.data: return
|
||||||
for chunk in self.data:
|
for chunk in self.data:
|
||||||
self.write(chunk)
|
self.write(chunk)
|
||||||
self.fp.flush()
|
|
||||||
Loading…
x
Reference in New Issue
Block a user