mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Added more valid request tests.
Found and fixed a couple read and readline related bugs.
This commit is contained in:
parent
ae025cd22b
commit
5af1273fc2
@ -3,6 +3,8 @@
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
@ -177,50 +179,69 @@ class Body(object):
|
||||
if not ret:
|
||||
raise StopIteration()
|
||||
return ret
|
||||
|
||||
def getsize(self, size):
|
||||
if size is None:
|
||||
return sys.maxint
|
||||
elif not isinstance(size, (int, long)):
|
||||
raise TypeError("size must be an integral type")
|
||||
elif size < 0:
|
||||
return sys.maxint
|
||||
return size
|
||||
|
||||
def read(self, size=None):
|
||||
if size is not None and not isinstance(size, (int, long)):
|
||||
raise TypeError("size must be an integral type")
|
||||
size = self.getsize(size)
|
||||
if size == 0:
|
||||
return ""
|
||||
|
||||
if size is not None and size < self.buf.tell():
|
||||
if size < self.buf.tell():
|
||||
data = self.buf.getvalue()
|
||||
ret, rest = data[:size], data[size:]
|
||||
self.buf.truncate(0)
|
||||
self.buf.write(rest)
|
||||
return ret
|
||||
|
||||
if size > 0:
|
||||
size -= self.buf.tell()
|
||||
else:
|
||||
size = None
|
||||
|
||||
ret = self.buf.getvalue() + self.reader.read(size=size)
|
||||
while size > self.buf.tell():
|
||||
data = self.reader.read(1024)
|
||||
if not len(data):
|
||||
break
|
||||
self.buf.write(data)
|
||||
|
||||
data = self.buf.getvalue()
|
||||
ret, rest = data[:size], data[size:]
|
||||
self.buf.truncate(0)
|
||||
self.buf.write(rest)
|
||||
return ret
|
||||
|
||||
def readline(self, size=None):
|
||||
size = self.getsize(size)
|
||||
if size == 0:
|
||||
return ""
|
||||
if size < 0:
|
||||
size = None
|
||||
|
||||
idx = -1
|
||||
idx = self.buf.getvalue().find("\n")
|
||||
while idx < 0:
|
||||
data = self.reader.read(1024)
|
||||
if not len(data):
|
||||
break
|
||||
self.buf.write(data)
|
||||
if size is not None and self.buf.tell() > size:
|
||||
idx = self.buf.getvalue().find("\n")
|
||||
if size < self.buf.tell():
|
||||
break
|
||||
idx = self.buf.getvalue().find("\r\n")
|
||||
|
||||
# If we didn't find it, and we got here, we've
|
||||
# exceeded size or run out of data.
|
||||
if idx < 0:
|
||||
rlen = min(size, self.buf.tell())
|
||||
else:
|
||||
rlen = idx + 1
|
||||
|
||||
if idx < 0 and size is not None:
|
||||
idx = size
|
||||
elif idx < 0:
|
||||
idx = self.buf.tell()
|
||||
# If rlen is beyond our size threshold, trim back
|
||||
if rlen > size:
|
||||
rlen = size
|
||||
|
||||
data = self.buf.getvalue()
|
||||
ret, rest = data[:idx], data[idx:]
|
||||
ret, rest = data[:rlen], data[rlen:]
|
||||
|
||||
self.buf.truncate(0)
|
||||
self.buf.write(rest)
|
||||
return ret
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import t
|
||||
import treq
|
||||
|
||||
import glob
|
||||
import os
|
||||
dirname = os.path.dirname(__file__)
|
||||
reqdir = os.path.join(dirname, "requests")
|
||||
|
||||
def load_py(fname):
|
||||
config = globals().copy()
|
||||
config["uri"] = treq.uri
|
||||
execfile(fname, config)
|
||||
return config["request"]
|
||||
|
||||
def test_http_parser():
|
||||
for fname in glob.glob(os.path.join(reqdir, "*.http")):
|
||||
expect = load_py(os.path.splitext(fname)[0] + ".py")
|
||||
req = treq.request(fname, expect)
|
||||
for case in req.gen_cases():
|
||||
yield case
|
||||
34
tests/001-test-valid-requests.py
Normal file
34
tests/001-test-valid-requests.py
Normal file
@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import t
|
||||
import treq
|
||||
|
||||
import glob
|
||||
import os
|
||||
dirname = os.path.dirname(__file__)
|
||||
reqdir = os.path.join(dirname, "requests", "valid")
|
||||
|
||||
def load_py(fname):
|
||||
config = globals().copy()
|
||||
config["uri"] = treq.uri
|
||||
execfile(fname, config)
|
||||
return config["request"]
|
||||
|
||||
def a_case(fname):
|
||||
expect = load_py(os.path.splitext(fname)[0] + ".py")
|
||||
req = treq.request(fname, expect)
|
||||
for case in req.gen_cases():
|
||||
case[0](*case[1:])
|
||||
|
||||
def test_http_parser():
|
||||
for fname in glob.glob(os.path.join(reqdir, "*.http")):
|
||||
if os.getenv("GUNS_BLAZING"):
|
||||
expect = load_py(os.path.splitext(fname)[0] + ".py")
|
||||
req = treq.request(fname, expect)
|
||||
for case in req.gen_cases():
|
||||
yield case
|
||||
else:
|
||||
yield (a_case, fname)
|
||||
4
tests/requests/valid/018.http
Normal file
4
tests/requests/valid/018.http
Normal file
@ -0,0 +1,4 @@
|
||||
GET /first HTTP/1.1\r\n
|
||||
\r\n
|
||||
GET /second HTTP/1.1\r\n
|
||||
\r\n
|
||||
17
tests/requests/valid/018.py
Normal file
17
tests/requests/valid/018.py
Normal file
@ -0,0 +1,17 @@
|
||||
req1 = {
|
||||
"method": "GET",
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}
|
||||
|
||||
req2 = {
|
||||
"method": "GET",
|
||||
"uri": uri("/second"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}
|
||||
|
||||
request = [req1, req2]
|
||||
4
tests/requests/valid/019.http
Normal file
4
tests/requests/valid/019.http
Normal file
@ -0,0 +1,4 @@
|
||||
GET /first HTTP/1.0\r\n
|
||||
\r\n
|
||||
GET /second HTTP/1.1\r\n
|
||||
\r\n
|
||||
7
tests/requests/valid/019.py
Normal file
7
tests/requests/valid/019.py
Normal file
@ -0,0 +1,7 @@
|
||||
request = {
|
||||
"method": "GET",
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 0),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}
|
||||
5
tests/requests/valid/020.http
Normal file
5
tests/requests/valid/020.http
Normal file
@ -0,0 +1,5 @@
|
||||
GET /first HTTP/1.0\r\n
|
||||
Content-Length: 24\r\n
|
||||
\r\n
|
||||
GET /second HTTP/1.1\r\n
|
||||
\r\n
|
||||
7
tests/requests/valid/020.py
Normal file
7
tests/requests/valid/020.py
Normal file
@ -0,0 +1,7 @@
|
||||
request = {
|
||||
"method": "GET",
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 0),
|
||||
"headers": [('CONTENT-LENGTH', '24')],
|
||||
"body": "GET /second HTTP/1.1\r\n\r\n"
|
||||
}
|
||||
5
tests/requests/valid/021.http
Normal file
5
tests/requests/valid/021.http
Normal file
@ -0,0 +1,5 @@
|
||||
GET /first HTTP/1.1\r\n
|
||||
Connection: Close\r\n
|
||||
\r\n
|
||||
GET /second HTTP/1.1\r\n
|
||||
\r\n
|
||||
7
tests/requests/valid/021.py
Normal file
7
tests/requests/valid/021.py
Normal file
@ -0,0 +1,7 @@
|
||||
request = {
|
||||
"method": "GET",
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 1),
|
||||
"headers": [("CONNECTION", "Close")],
|
||||
"body": ""
|
||||
}
|
||||
5
tests/requests/valid/022.http
Normal file
5
tests/requests/valid/022.http
Normal file
@ -0,0 +1,5 @@
|
||||
GET /first HTTP/1.0\r\n
|
||||
Connection: Keep-Alive\r\n
|
||||
\r\n
|
||||
GET /second HTTP/1.1\r\n
|
||||
\r\n
|
||||
17
tests/requests/valid/022.py
Normal file
17
tests/requests/valid/022.py
Normal file
@ -0,0 +1,17 @@
|
||||
req1 = {
|
||||
"method": "GET",
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 0),
|
||||
"headers": [("CONNECTION", "Keep-Alive")],
|
||||
"body": ""
|
||||
}
|
||||
|
||||
req2 = {
|
||||
"method": "GET",
|
||||
"uri": uri("/second"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}
|
||||
|
||||
request = [req1, req2]
|
||||
11
tests/requests/valid/023.http
Normal file
11
tests/requests/valid/023.http
Normal file
@ -0,0 +1,11 @@
|
||||
POST /two_chunks_mult_zero_end HTTP/1.1\r\n
|
||||
Transfer-Encoding: chunked\r\n
|
||||
\r\n
|
||||
5\r\n
|
||||
hello\r\n
|
||||
6\r\n
|
||||
world\r\n
|
||||
000\r\n
|
||||
\r\n
|
||||
GET /second HTTP/1.1\r\n
|
||||
\r\n
|
||||
19
tests/requests/valid/023.py
Normal file
19
tests/requests/valid/023.py
Normal file
@ -0,0 +1,19 @@
|
||||
req1 = {
|
||||
"method": "POST",
|
||||
"uri": uri("/two_chunks_mult_zero_end"),
|
||||
"version": (1, 1),
|
||||
"headers": [
|
||||
("TRANSFER-ENCODING", "chunked")
|
||||
],
|
||||
"body": "hello world"
|
||||
}
|
||||
|
||||
req2 = {
|
||||
"method": "GET",
|
||||
"uri": uri("/second"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}
|
||||
|
||||
request = [req1, req2]
|
||||
@ -89,7 +89,7 @@ class request(object):
|
||||
return 1
|
||||
|
||||
def size_small_random(self):
|
||||
return random.randint(0, 2)
|
||||
return random.randint(0, 4)
|
||||
|
||||
def size_random(self):
|
||||
return random.randint(1, 4096)
|
||||
@ -117,6 +117,7 @@ class request(object):
|
||||
if not data:
|
||||
count -= 1
|
||||
if count <= 0:
|
||||
print "BOD: %r" % body
|
||||
raise AssertionError("Unexpected apparent EOF")
|
||||
|
||||
if len(body):
|
||||
@ -201,14 +202,7 @@ class request(object):
|
||||
for sz in sizers
|
||||
for sn in senders
|
||||
]
|
||||
# Strip out match_readlines, match_iter for all but one sizer
|
||||
cfgs = [
|
||||
(mt, sz, sn)
|
||||
for (mt, sz, sn) in cfgs
|
||||
if mt in [self.match_readlines, self.match_iter]
|
||||
and sz != self.size_all
|
||||
]
|
||||
|
||||
|
||||
ret = []
|
||||
for (mt, sz, sn) in cfgs:
|
||||
mtn = mt.func_name[6:]
|
||||
@ -223,43 +217,12 @@ class request(object):
|
||||
|
||||
def check(self, sender, sizer, matcher):
|
||||
cases = self.expect[:]
|
||||
ended = False
|
||||
try:
|
||||
p = RequestParser(sender())
|
||||
except Exception, e:
|
||||
if not isinstance(cases[0], Exception):
|
||||
raise
|
||||
self.same_error(e, cases[0])
|
||||
t.eq(len(casese), 1)
|
||||
while True:
|
||||
try:
|
||||
req = p.next()
|
||||
except StopIteration, e:
|
||||
t.eq(len(cases), 0)
|
||||
ended = True
|
||||
break
|
||||
except ParseException, e:
|
||||
if not issubclass(cases[0], Exception):
|
||||
raise
|
||||
self.same_error(e, cases.pop(0))
|
||||
t.eq(len(cases), 0)
|
||||
return
|
||||
else:
|
||||
self.same(req, sizer, matcher, cases.pop(0))
|
||||
p = RequestParser(sender())
|
||||
for req in p:
|
||||
self.same(req, sizer, matcher, cases.pop(0))
|
||||
t.eq(len(cases), 0)
|
||||
t.eq(ended, True)
|
||||
|
||||
def same(self, req, sizer, matcher, exp):
|
||||
if isinstance(req, Exception):
|
||||
self.same_error(req, exp)
|
||||
else:
|
||||
self.same_obj(req, sizer, matcher, exp)
|
||||
|
||||
def same_error(self, req, exp):
|
||||
t.istype(req, Exception)
|
||||
t.istype(req, exp)
|
||||
|
||||
def same_obj(self, req, sizer, matcher, exp):
|
||||
t.eq(req.method, exp["method"])
|
||||
t.eq(req.uri, exp["uri"]["raw"])
|
||||
t.eq(req.scheme, exp["uri"]["scheme"])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user