all tests pass under python 3

This commit is contained in:
benoitc 2012-10-24 22:07:35 +02:00
parent 60644b12af
commit 8d453fb341
47 changed files with 276 additions and 293 deletions

View File

@ -74,10 +74,10 @@ def reload_django_settings():
app_mod = util.import_module(app[:-2]) app_mod = util.import_module(app[:-2])
appdir = os.path.dirname(app_mod.__file__) appdir = os.path.dirname(app_mod.__file__)
app_subdirs = os.listdir(appdir) app_subdirs = os.listdir(appdir)
app_subdirs.sort()
name_pattern = re.compile(r'[a-zA-Z]\w*') name_pattern = re.compile(r'[a-zA-Z]\w*')
for d in app_subdirs: for d in sorted(app_subdirs):
if name_pattern.match(d) and os.path.isdir(os.path.join(appdir, d)): if (name_pattern.match(d) and
os.path.isdir(os.path.join(appdir, d))):
new_installed_apps.append('%s.%s' % (app[:-2], d)) new_installed_apps.append('%s.%s' % (app[:-2], d))
else: else:
new_installed_apps.append(app) new_installed_apps.append(app)

View File

@ -99,7 +99,10 @@ class Arbiter(object):
if self.cfg.debug: if self.cfg.debug:
self.log.debug("Current configuration:") self.log.debug("Current configuration:")
for config, value in sorted(self.cfg.settings.iteritems()):
for config, value in sorted(self.cfg.settings.items(),
key=lambda setting: setting[1]):
self.log.debug(" %s: %s", config, value.value) self.log.debug(" %s: %s", config, value.value)
if self.cfg.preload_app: if self.cfg.preload_app:
@ -436,7 +439,7 @@ class Arbiter(object):
self.spawn_workers() self.spawn_workers()
workers = self.WORKERS.items() workers = self.WORKERS.items()
workers.sort(key=lambda w: w[1].age) workers = sorted(workers, key=lambda w: w[1].age)
while len(workers) > self.num_workers: while len(workers) > self.num_workers:
(pid, _) = workers.pop(0) (pid, _) = workers.pop(0)
self.kill_worker(pid, signal.SIGQUIT) self.kill_worker(pid, signal.SIGQUIT)

View File

@ -15,7 +15,7 @@ import types
from gunicorn import __version__ from gunicorn import __version__
from gunicorn.errors import ConfigError from gunicorn.errors import ConfigError
from gunicorn import util from gunicorn import util
from gunicorn.six import string_types from gunicorn.six import string_types, integer_types, bytes_to_str
KNOWN_SETTINGS = [] KNOWN_SETTINGS = []
@ -62,10 +62,12 @@ class Config(object):
} }
parser = optparse.OptionParser(**kwargs) parser = optparse.OptionParser(**kwargs)
keys = self.settings.keys() keys = list(self.settings)
def sorter(k): def sorter(k):
return (self.settings[k].section, self.settings[k].order) return (self.settings[k].section, self.settings[k].order)
keys.sort(key=sorter)
keys = sorted(self.settings, key=self.settings.__getitem__)
for k in keys: for k in keys:
self.settings[k].add_option(parser) self.settings[k].add_option(parser)
return parser return parser
@ -85,7 +87,7 @@ class Config(object):
@property @property
def address(self): def address(self):
bind = self.settings['bind'].get() bind = self.settings['bind'].get()
return util.parse_address(util.to_bytestring(bind)) return util.parse_address(bytes_to_str(bind))
@property @property
def uid(self): def uid(self):
@ -179,8 +181,15 @@ class Setting(object):
assert callable(self.validator), "Invalid validator: %s" % self.name assert callable(self.validator), "Invalid validator: %s" % self.name
self.value = self.validator(val) self.value = self.validator(val)
def __lt__(self, other):
return (self.section == other.section and
self.order < other.order)
__cmp__ = __lt__
Setting = SettingMeta('Setting', (Setting,), {})
def validate_bool(val): def validate_bool(val):
if isinstance(val, types.BooleanType): if isinstance(val, bool):
return val return val
if not isinstance(val, string_types): if not isinstance(val, string_types):
raise TypeError("Invalid type for casting: %s" % val) raise TypeError("Invalid type for casting: %s" % val)
@ -197,7 +206,7 @@ def validate_dict(val):
return val return val
def validate_pos_int(val): def validate_pos_int(val):
if not isinstance(val, (types.IntType, types.LongType)): if not isinstance(val, integer_types):
val = int(val, 0) val = int(val, 0)
else: else:
# Booleans are ints! # Booleans are ints!

View File

@ -4,7 +4,7 @@
# See the NOTICE for more information. # See the NOTICE for more information.
class HaltServer(Exception): class HaltServer(BaseException):
def __init__(self, reason, exit_status=1): def __init__(self, reason, exit_status=1):
self.reason = reason self.reason = reason
self.exit_status = exit_status self.exit_status = exit_status
@ -12,5 +12,5 @@ class HaltServer(Exception):
def __str__(self): def __str__(self):
return "<HaltServer %r %d>" % (self.reason, self.exit_status) return "<HaltServer %r %d>" % (self.reason, self.exit_status)
class ConfigError(Exception): class ConfigError(BaseException):
""" Exception raised on config error """ """ Exception raised on config error """

View File

@ -7,16 +7,16 @@ import sys
from gunicorn.http.errors import (NoMoreData, ChunkMissingTerminator, from gunicorn.http.errors import (NoMoreData, ChunkMissingTerminator,
InvalidChunkSize) InvalidChunkSize)
from gunicorn.six import StringIO, bytes_to_str, integer_types from gunicorn import six
class ChunkedReader(object): class ChunkedReader(object):
def __init__(self, req, unreader): def __init__(self, req, unreader):
self.req = req self.req = req
self.parser = self.parse_chunked(unreader) self.parser = self.parse_chunked(unreader)
self.buf = StringIO() self.buf = six.BytesIO()
def read(self, size): def read(self, size):
if not isinstance(size, integer_types): if not isinstance(size, six.integer_types):
raise TypeError("size must be an integral type") raise TypeError("size must be an integral type")
if size <= 0: if size <= 0:
raise ValueError("Size must be positive.") raise ValueError("Size must be positive.")
@ -26,19 +26,19 @@ class ChunkedReader(object):
if self.parser: if self.parser:
while self.buf.tell() < size: while self.buf.tell() < size:
try: try:
self.buf.write(self.parser.next()) self.buf.write(six.next(self.parser))
except StopIteration: except StopIteration:
self.parser = None self.parser = None
break break
data = self.buf.getvalue() data = self.buf.getvalue()
ret, rest = data[:size], data[size:] ret, rest = data[:size], data[size:]
self.buf.truncate(0) self.buf = six.BytesIO()
self.buf.write(rest) self.buf.write(rest)
return ret return ret
def parse_trailers(self, unreader, data): def parse_trailers(self, unreader, data):
buf = StringIO() buf = six.BytesIO()
buf.write(data) buf.write(data)
idx = buf.getvalue().find(b"\r\n\r\n") idx = buf.getvalue().find(b"\r\n\r\n")
@ -50,8 +50,7 @@ class ChunkedReader(object):
if done: if done:
unreader.unread(buf.getvalue()[2:]) unreader.unread(buf.getvalue()[2:])
return b"" return b""
self.req.trailers = self.req.parse_headers( self.req.trailers = self.req.parse_headers(buf.getvalue()[:idx])
bytes_to_str(buf.getvalue()[:idx]))
unreader.unread(buf.getvalue()[idx+4:]) unreader.unread(buf.getvalue()[idx+4:])
def parse_chunked(self, unreader): def parse_chunked(self, unreader):
@ -73,7 +72,7 @@ class ChunkedReader(object):
(size, rest) = self.parse_chunk_size(unreader, data=rest[2:]) (size, rest) = self.parse_chunk_size(unreader, data=rest[2:])
def parse_chunk_size(self, unreader, data=None): def parse_chunk_size(self, unreader, data=None):
buf = StringIO() buf = six.BytesIO()
if data is not None: if data is not None:
buf.write(data) buf.write(data)
@ -111,7 +110,7 @@ class LengthReader(object):
self.length = length self.length = length
def read(self, size): def read(self, size):
if not isinstance(size, integer_types): if not isinstance(size, six.integer_types):
raise TypeError("size must be an integral type") raise TypeError("size must be an integral type")
size = min(self.length, size) size = min(self.length, size)
@ -121,7 +120,7 @@ class LengthReader(object):
return b"" return b""
buf = StringIO() buf = six.BytesIO()
data = self.unreader.read() data = self.unreader.read()
while data: while data:
buf.write(data) buf.write(data)
@ -138,21 +137,21 @@ class LengthReader(object):
class EOFReader(object): class EOFReader(object):
def __init__(self, unreader): def __init__(self, unreader):
self.unreader = unreader self.unreader = unreader
self.buf = StringIO() self.buf = six.BytesIO()
self.finished = False self.finished = False
def read(self, size): def read(self, size):
if not isinstance(size, integer_types): if not isinstance(size, six.integer_types):
raise TypeError("size must be an integral type") raise TypeError("size must be an integral type")
if size < 0: if size < 0:
raise ValueError("Size must be positive.") raise ValueError("Size must be positive.")
if size == 0: if size == 0:
return "" return b""
if self.finished: if self.finished:
data = self.buf.getvalue() data = self.buf.getvalue()
ret, rest = data[:size], data[size:] ret, rest = data[:size], data[size:]
self.buf.truncate(0) self.buf = six.BytesIO()
self.buf.write(rest) self.buf.write(rest)
return ret return ret
@ -168,31 +167,32 @@ class EOFReader(object):
data = self.buf.getvalue() data = self.buf.getvalue()
ret, rest = data[:size], data[size:] ret, rest = data[:size], data[size:]
self.buf.truncate(0) self.buf = six.BytesIO()
self.buf.write(rest) self.buf.write(rest)
return ret return ret
class Body(object): class Body(object):
def __init__(self, reader): def __init__(self, reader):
self.reader = reader self.reader = reader
self.buf = StringIO() self.buf = six.BytesIO()
def __iter__(self): def __iter__(self):
return self return self
def next(self): def __next__(self):
ret = self.readline() ret = self.readline()
if not ret: if not ret:
raise StopIteration() raise StopIteration()
return ret return ret
next = __next__
def getsize(self, size): def getsize(self, size):
if size is None: if size is None:
return sys.maxint return six.MAXSIZE
elif not isinstance(size, integer_types): elif not isinstance(size, six.integer_types):
raise TypeError("size must be an integral type") raise TypeError("size must be an integral type")
elif size < 0: elif size < 0:
return sys.maxint return six.MAXSIZE
return size return size
def read(self, size=None): def read(self, size=None):
@ -203,7 +203,7 @@ class Body(object):
if size < self.buf.tell(): if size < self.buf.tell():
data = self.buf.getvalue() data = self.buf.getvalue()
ret, rest = data[:size], data[size:] ret, rest = data[:size], data[size:]
self.buf.truncate(0) self.buf = six.BytesIO()
self.buf.write(rest) self.buf.write(rest)
return ret return ret
@ -215,7 +215,7 @@ class Body(object):
data = self.buf.getvalue() data = self.buf.getvalue()
ret, rest = data[:size], data[size:] ret, rest = data[:size], data[size:]
self.buf.truncate(0) self.buf = six.BytesIO()
self.buf.write(rest) self.buf.write(rest)
return ret return ret
@ -225,7 +225,7 @@ class Body(object):
return b"" return b""
line = self.buf.getvalue() line = self.buf.getvalue()
self.buf.truncate(0) self.buf = six.BytesIO()
if len(line) < size: if len(line) < size:
line += self.reader.read(size - len(line)) line += self.reader.read(size - len(line))
extra_buf_data = line[size:] extra_buf_data = line[size:]

View File

@ -3,12 +3,13 @@
# This file is part of gunicorn released under the MIT license. # This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information. # See the NOTICE for more information.
class ParseException(Exception): class ParseException(BaseException):
pass pass
class NoMoreData(IOError): class NoMoreData(IOError):
def __init__(self, buf=None): def __init__(self, buf=None):
self.buf = buf self.buf = buf
def __str__(self): def __str__(self):
return "No more data after: %r" % self.buf return "No more data after: %r" % self.buf

View File

@ -13,7 +13,7 @@ from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, NoMoreData, \
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, \ InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, \
LimitRequestLine, LimitRequestHeaders LimitRequestLine, LimitRequestHeaders
from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest
from gunicorn.six import StringIO, urlsplit, bytes_to_str from gunicorn.six import BytesIO, urlsplit, bytes_to_str
MAX_REQUEST_LINE = 8190 MAX_REQUEST_LINE = 8190
MAX_HEADERS = 32768 MAX_HEADERS = 32768
@ -148,7 +148,6 @@ class Request(Message):
self.req_number = req_number self.req_number = req_number
self.proxy_protocol_info = None self.proxy_protocol_info = None
super(Request, self).__init__(cfg, unreader) super(Request, self).__init__(cfg, unreader)
@ -161,7 +160,7 @@ class Request(Message):
buf.write(data) buf.write(data)
def parse(self, unreader): def parse(self, unreader):
buf = StringIO() buf = BytesIO()
self.get_data(unreader, buf, stop=True) self.get_data(unreader, buf, stop=True)
# get request line # get request line
@ -170,12 +169,12 @@ class Request(Message):
# proxy protocol # proxy protocol
if self.proxy_protocol(bytes_to_str(line)): if self.proxy_protocol(bytes_to_str(line)):
# get next request line # get next request line
buf = StringIO() buf = BytesIO()
buf.write(rbuf) buf.write(rbuf)
line, rbuf = self.read_line(unreader, buf, self.limit_request_line) line, rbuf = self.read_line(unreader, buf, self.limit_request_line)
self.parse_request_line(bytes_to_str(line)) self.parse_request_line(bytes_to_str(line))
buf = StringIO() buf = BytesIO()
buf.write(rbuf) buf.write(rbuf)
# Headers # Headers
@ -202,7 +201,7 @@ class Request(Message):
self.headers = self.parse_headers(data[:idx]) self.headers = self.parse_headers(data[:idx])
ret = data[idx+4:] ret = data[idx+4:]
buf = StringIO() buf = BytesIO()
return ret return ret
def read_line(self, unreader, buf, limit=0): def read_line(self, unreader, buf, limit=0):

View File

@ -22,7 +22,7 @@ class Parser(object):
def __iter__(self): def __iter__(self):
return self return self
def next(self): def __next__(self):
# Stop if HTTP dictates a stop. # Stop if HTTP dictates a stop.
if self.mesg and self.mesg.should_close(): if self.mesg and self.mesg.should_close():
raise StopIteration() raise StopIteration()
@ -33,6 +33,7 @@ class Parser(object):
while data: while data:
data = self.mesg.body.read(8192) data = self.mesg.body.read(8192)
# Parse the next request # Parse the next request
self.req_count += 1 self.req_count += 1
self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count) self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count)
@ -40,6 +41,8 @@ class Parser(object):
raise StopIteration() raise StopIteration()
return self.mesg return self.mesg
next = __next__
class RequestParser(Parser): class RequestParser(Parser):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(RequestParser, self).__init__(Request, *args, **kwargs) super(RequestParser, self).__init__(Request, *args, **kwargs)

View File

@ -5,44 +5,47 @@
import os import os
from gunicorn.six import integer_types, StringIO from gunicorn import six
# Classes that can undo reading data from # Classes that can undo reading data from
# a given type of data source. # a given type of data source.
class Unreader(object): class Unreader(object):
def __init__(self): def __init__(self):
self.buf = StringIO() self.buf = six.BytesIO()
def chunk(self): def chunk(self):
raise NotImplementedError() raise NotImplementedError()
def read(self, size=None): def read(self, size=None):
if size is not None and not isinstance(size, integer_types): if size is not None and not isinstance(size, six.integer_types):
raise TypeError("size parameter must be an int or long.") raise TypeError("size parameter must be an int or long.")
if size == 0:
return "" if size is not None:
if size < 0: if size == 0:
size = None return b""
if size < 0:
size = None
self.buf.seek(0, os.SEEK_END) self.buf.seek(0, os.SEEK_END)
if size is None and self.buf.tell(): if size is None and self.buf.tell():
ret = self.buf.getvalue() ret = self.buf.getvalue()
self.buf.truncate(0) self.buf = six.BytesIO()
return ret return ret
if size is None: if size is None:
return self.chunk() d = self.chunk()
return d
while self.buf.tell() < size: while self.buf.tell() < size:
chunk = self.chunk() chunk = self.chunk()
if not len(chunk): if not len(chunk):
ret = self.buf.getvalue() ret = self.buf.getvalue()
self.buf.truncate(0) self.buf = six.BytesIO()
return ret return ret
self.buf.write(chunk) self.buf.write(chunk)
data = self.buf.getvalue() data = self.buf.getvalue()
self.buf.truncate(0) self.buf = six.BytesIO()
self.buf.write(data[size:]) self.buf.write(data[size:])
return data[:size] return data[:size]
@ -66,9 +69,9 @@ class IterUnreader(Unreader):
def chunk(self): def chunk(self):
if not self.iter: if not self.iter:
return "" return b""
try: try:
return self.iter.next() return six.next(self.iter)
except StopIteration: except StopIteration:
self.iter = None self.iter = None
return "" return b""

View File

@ -281,10 +281,14 @@ _add_doc(u, """Text literal""")
if PY3: if PY3:
def execfile_(fname, *args):
return exec(compile(open(fname, 'rb').read(), fname, 'exec'), *args)
import builtins import builtins
exec_ = getattr(builtins, "exec") exec_ = getattr(builtins, "exec")
def reraise(tp, value, tb=None): def reraise(tp, value, tb=None):
if value.__traceback__ is not tb: if value.__traceback__ is not tb:
raise value.with_traceback(tb) raise value.with_traceback(tb)
@ -294,10 +298,6 @@ if PY3:
print_ = getattr(builtins, "print") print_ = getattr(builtins, "print")
del builtins del builtins
def execfile_(file, globals=globals(), locals=locals()):
with open(file, "r") as fh:
exec_(fh.read()+"\n", globals, locals)
else: else:
def exec_(code, globs=None, locs=None): def exec_(code, globs=None, locs=None):
"""Execute code in a namespace.""" """Execute code in a namespace."""
@ -373,33 +373,26 @@ def with_metaclass(meta, base=object):
# specific to gunicorn # specific to gunicorn
if PY3: if PY3:
import io
StringIO = io.BytesIO
def bytes_to_str(b): def bytes_to_str(b):
if isinstance(b, text_type):
return b
return str(b, 'latin1') return str(b, 'latin1')
import urllib.parse import urllib.parse
unquote = urllib.parse.unquote unquote = urllib.parse.unquote
urlsplit = urllib.parse.urlsplit urlsplit = urllib.parse.urlsplit
urlparse = urllib.parse.urlparse
else: else:
try: def bytes_to_str(s):
import cStringIO as StringIO
except ImportError:
import StringIO
StringIO = StringIO
def bytestring(s):
if isinstance(s, unicode): if isinstance(s, unicode):
return s.encode('utf-8') return s.encode('utf-8')
return s return s
import urlparse as orig_urlparse import urlparse as orig_urlparse
urlsplit = orig_urlparse.urlsplit urlsplit = orig_urlparse.urlsplit
urlparse = orig_urlparse.urlparse
import urllib import urllib
urlunquote = urllib.unquote urlunquote = urllib.unquote

View File

@ -311,15 +311,6 @@ def http_date(timestamp=None):
hh, mm, ss) hh, mm, ss)
return s return s
def to_bytestring(s):
""" convert to bytestring an unicode """
if not isinstance(s, string_types):
return s
if isinstance(s, unicode):
return s.encode('utf-8')
else:
return s
def is_hoppish(header): def is_hoppish(header):
return header.lower().strip() in hop_headers return header.lower().strip() in hop_headers

View File

@ -13,6 +13,7 @@ import gunicorn.http as http
import gunicorn.http.wsgi as wsgi import gunicorn.http.wsgi as wsgi
import gunicorn.util as util import gunicorn.util as util
import gunicorn.workers.base as base import gunicorn.workers.base as base
from gunicorn import six
ALREADY_HANDLED = object() ALREADY_HANDLED = object()
@ -32,14 +33,14 @@ class AsyncWorker(base.Worker):
parser = http.RequestParser(self.cfg, client) parser = http.RequestParser(self.cfg, client)
try: try:
if not self.cfg.keepalive: if not self.cfg.keepalive:
req = parser.next() req = six.next(parser)
self.handle_request(req, client, addr) self.handle_request(req, client, addr)
else: else:
# keepalive loop # keepalive loop
while True: while True:
req = None req = None
with self.timeout_ctx(): with self.timeout_ctx():
req = parser.next() req = six.next(parser)
if not req: if not req:
break break
self.handle_request(req, client, addr) self.handle_request(req, client, addr)

View File

@ -18,6 +18,7 @@ InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, \
LimitRequestLine, LimitRequestHeaders LimitRequestLine, LimitRequestHeaders
from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest
from gunicorn.http.wsgi import default_environ, Response from gunicorn.http.wsgi import default_environ, Response
from gunicorn.six import MAXSIZE
class Worker(object): class Worker(object):
@ -43,7 +44,7 @@ class Worker(object):
self.booted = False self.booted = False
self.nr = 0 self.nr = 0
self.max_requests = cfg.max_requests or sys.maxint self.max_requests = cfg.max_requests or MAXSIZE
self.alive = True self.alive = True
self.log = log self.log = log
self.debug = cfg.debug self.debug = cfg.debug

View File

@ -14,6 +14,7 @@ import gunicorn.http as http
import gunicorn.http.wsgi as wsgi import gunicorn.http.wsgi as wsgi
import gunicorn.util as util import gunicorn.util as util
import gunicorn.workers.base as base import gunicorn.workers.base as base
from gunicorn import six
class SyncWorker(base.Worker): class SyncWorker(base.Worker):
@ -69,7 +70,7 @@ class SyncWorker(base.Worker):
try: try:
client.settimeout(self.cfg.timeout) client.settimeout(self.cfg.timeout)
parser = http.RequestParser(self.cfg, client) parser = http.RequestParser(self.cfg, client)
req = parser.next() req = six.next(parser)
self.handle_request(req, client, addr) self.handle_request(req, client, addr)
except http.errors.NoMoreData as e: except http.errors.NoMoreData as e:
self.log.debug("Ignored premature client disconnection. %s", e) self.log.debug("Ignored premature client disconnection. %s", e)

View File

@ -1,61 +0,0 @@
from StringIO import StringIO
import t
from gunicorn.http.body import Body
def assert_readline(payload, size, expected):
body = Body(StringIO(payload))
t.eq(body.readline(size), expected)
def test_readline_empty_body():
assert_readline("", None, "")
assert_readline("", 1, "")
def test_readline_zero_size():
assert_readline("abc", 0, "")
assert_readline("\n", 0, "")
def test_readline_new_line_before_size():
body = Body(StringIO("abc\ndef"))
t.eq(body.readline(4), "abc\n")
t.eq(body.readline(), "def")
def test_readline_new_line_after_size():
body = Body(StringIO("abc\ndef"))
t.eq(body.readline(2), "ab")
t.eq(body.readline(), "c\n")
def test_readline_no_new_line():
body = Body(StringIO("abcdef"))
t.eq(body.readline(), "abcdef")
body = Body(StringIO("abcdef"))
t.eq(body.readline(2), "ab")
t.eq(body.readline(2), "cd")
t.eq(body.readline(2), "ef")
def test_readline_buffer_loaded():
reader = StringIO("abc\ndef")
body = Body(reader)
body.read(1) # load internal buffer
reader.write("g\nhi")
reader.seek(7)
t.eq(body.readline(), "bc\n")
t.eq(body.readline(), "defg\n")
t.eq(body.readline(), "hi")
def test_readline_buffer_loaded_with_size():
body = Body(StringIO("abc\ndef"))
body.read(1) # load internal buffer
t.eq(body.readline(2), "bc")
t.eq(body.readline(2), "\n")
t.eq(body.readline(2), "de")
t.eq(body.readline(2), "f")

View File

@ -7,5 +7,5 @@ request = {
("CONTENT-TYPE", "application/json"), ("CONTENT-TYPE", "application/json"),
("CONTENT-LENGTH", "14") ("CONTENT-LENGTH", "14")
], ],
"body": '{"nom": "nom"}' "body": b'{"nom": "nom"}'
} }

View File

@ -7,5 +7,5 @@ request = {
("HOST", "0.0.0.0=5000"), ("HOST", "0.0.0.0=5000"),
("ACCEPT", "*/*") ("ACCEPT", "*/*")
], ],
"body": "" "body": b""
} }

View File

@ -12,5 +12,5 @@ request = {
("KEEP-ALIVE", "300"), ("KEEP-ALIVE", "300"),
("CONNECTION", "keep-alive") ("CONNECTION", "keep-alive")
], ],
"body": "" "body": b""
} }

View File

@ -5,5 +5,5 @@ request = {
"headers": [ "headers": [
("AAAAAAAAAAAAA", "++++++++++") ("AAAAAAAAAAAAA", "++++++++++")
], ],
"body": "" "body": b""
} }

View File

@ -3,5 +3,5 @@ request = {
"uri": uri("/forums/1/topics/2375?page=1#posts-17408"), "uri": uri("/forums/1/topics/2375?page=1#posts-17408"),
"version": (1, 1), "version": (1, 1),
"headers": [], "headers": [],
"body": "" "body": b""
} }

View File

@ -3,5 +3,5 @@ request = {
"uri": uri("/get_no_headers_no_body/world"), "uri": uri("/get_no_headers_no_body/world"),
"version": (1, 1), "version": (1, 1),
"headers": [], "headers": [],
"body": "" "body": b""
} }

View File

@ -5,5 +5,5 @@ request = {
"headers": [ "headers": [
("ACCEPT", "*/*") ("ACCEPT", "*/*")
], ],
"body": "" "body": b""
} }

View File

@ -5,5 +5,5 @@ request = {
"headers": [ "headers": [
("CONTENT-LENGTH", "5") ("CONTENT-LENGTH", "5")
], ],
"body": "HELLO" "body": b"HELLO"
} }

View File

@ -7,5 +7,5 @@ request = {
("TRANSFER-ENCODING", "identity"), ("TRANSFER-ENCODING", "identity"),
("CONTENT-LENGTH", "5") ("CONTENT-LENGTH", "5")
], ],
"body": "World" "body": b"World"
} }

View File

@ -5,5 +5,5 @@ request = {
"headers": [ "headers": [
("TRANSFER-ENCODING", "chunked"), ("TRANSFER-ENCODING", "chunked"),
], ],
"body": "all your base are belong to us" "body": b"all your base are belong to us"
} }

View File

@ -5,5 +5,5 @@ request = {
"headers": [ "headers": [
("TRANSFER-ENCODING", "chunked") ("TRANSFER-ENCODING", "chunked")
], ],
"body": "hello world" "body": b"hello world"
} }

View File

@ -5,9 +5,9 @@ request = {
"headers": [ "headers": [
("TRANSFER-ENCODING", "chunked") ("TRANSFER-ENCODING", "chunked")
], ],
"body": "hello world", "body": b"hello world",
"trailers": [ "trailers": [
("VARY", "*"), ("VARY", "*"),
("CONTENT-TYPE", "text/plain") ("CONTENT-TYPE", "text/plain")
] ]
} }

View File

@ -5,5 +5,5 @@ request = {
"headers": [ "headers": [
("TRANSFER-ENCODING", "chunked") ("TRANSFER-ENCODING", "chunked")
], ],
"body": "hello world" "body": b"hello world"
} }

View File

@ -3,5 +3,5 @@ request = {
"uri": uri('/with_"quotes"?foo="bar"'), "uri": uri('/with_"quotes"?foo="bar"'),
"version": (1, 1), "version": (1, 1),
"headers": [], "headers": [],
"body": "" "body": b""
} }

View File

@ -7,5 +7,5 @@ request = {
("USER-AGENT", "ApacheBench/2.3"), ("USER-AGENT", "ApacheBench/2.3"),
("ACCEPT", "*/*") ("ACCEPT", "*/*")
], ],
"body": "" "body": b""
} }

View File

@ -36,5 +36,5 @@ request = {
"uri": uri("/"), "uri": uri("/"),
"version": (1, 1), "version": (1, 1),
"headers": [("X-SSL-CERT", certificate)], "headers": [("X-SSL-CERT", certificate)],
"body": "" "body": b""
} }

View File

@ -6,5 +6,5 @@ request = {
("IF-MATCH", "bazinga!"), ("IF-MATCH", "bazinga!"),
("IF-MATCH", "large-sound") ("IF-MATCH", "large-sound")
], ],
"body": "" "body": b""
} }

View File

@ -3,7 +3,7 @@ req1 = {
"uri": uri("/first"), "uri": uri("/first"),
"version": (1, 1), "version": (1, 1),
"headers": [], "headers": [],
"body": "" "body": b""
} }
req2 = { req2 = {
@ -11,7 +11,7 @@ req2 = {
"uri": uri("/second"), "uri": uri("/second"),
"version": (1, 1), "version": (1, 1),
"headers": [], "headers": [],
"body": "" "body": b""
} }
request = [req1, req2] request = [req1, req2]

View File

@ -3,5 +3,5 @@ request = {
"uri": uri("/first"), "uri": uri("/first"),
"version": (1, 0), "version": (1, 0),
"headers": [], "headers": [],
"body": "" "body": b""
} }

View File

@ -3,5 +3,5 @@ request = {
"uri": uri("/first"), "uri": uri("/first"),
"version": (1, 0), "version": (1, 0),
"headers": [('CONTENT-LENGTH', '24')], "headers": [('CONTENT-LENGTH', '24')],
"body": "GET /second HTTP/1.1\r\n\r\n" "body": b"GET /second HTTP/1.1\r\n\r\n"
} }

View File

@ -3,5 +3,5 @@ request = {
"uri": uri("/first"), "uri": uri("/first"),
"version": (1, 1), "version": (1, 1),
"headers": [("CONNECTION", "Close")], "headers": [("CONNECTION", "Close")],
"body": "" "body": b""
} }

View File

@ -3,7 +3,7 @@ req1 = {
"uri": uri("/first"), "uri": uri("/first"),
"version": (1, 0), "version": (1, 0),
"headers": [("CONNECTION", "Keep-Alive")], "headers": [("CONNECTION", "Keep-Alive")],
"body": "" "body": b""
} }
req2 = { req2 = {
@ -11,7 +11,7 @@ req2 = {
"uri": uri("/second"), "uri": uri("/second"),
"version": (1, 1), "version": (1, 1),
"headers": [], "headers": [],
"body": "" "body": b""
} }
request = [req1, req2] request = [req1, req2]

View File

@ -5,7 +5,7 @@ req1 = {
"headers": [ "headers": [
("TRANSFER-ENCODING", "chunked") ("TRANSFER-ENCODING", "chunked")
], ],
"body": "hello world" "body": b"hello world"
} }
req2 = { req2 = {
@ -13,7 +13,7 @@ req2 = {
"uri": uri("/second"), "uri": uri("/second"),
"version": (1, 1), "version": (1, 1),
"headers": [], "headers": [],
"body": "" "body": b""
} }
request = [req1, req2] request = [req1, req2]

View File

@ -6,7 +6,7 @@ req1 = {
("CONTENT-LENGTH", "-1"), ("CONTENT-LENGTH", "-1"),
("TRANSFER-ENCODING", "chunked") ("TRANSFER-ENCODING", "chunked")
], ],
"body": "hello world" "body": b"hello world"
} }
req2 = { req2 = {
@ -17,7 +17,7 @@ req2 = {
("TRANSFER-ENCODING", "chunked"), ("TRANSFER-ENCODING", "chunked"),
("CONTENT-LENGTH", "-1"), ("CONTENT-LENGTH", "-1"),
], ],
"body": "hello world" "body": b"hello world"
} }
request = [req1, req2] request = [req1, req2]

View File

@ -12,5 +12,5 @@ request = {
("CONTENT-TYPE", "application/json"), ("CONTENT-TYPE", "application/json"),
("CONTENT-LENGTH", "14") ("CONTENT-LENGTH", "14")
], ],
"body": '{"nom": "nom"}' "body": b'{"nom": "nom"}'
} }

View File

@ -13,7 +13,7 @@ req1 = {
("CONTENT-LENGTH", "14"), ("CONTENT-LENGTH", "14"),
("CONNECTION", "keep-alive") ("CONNECTION", "keep-alive")
], ],
"body": '{"nom": "nom"}' "body": b'{"nom": "nom"}'
} }
@ -24,7 +24,7 @@ req2 = {
"headers": [ "headers": [
("TRANSFER-ENCODING", "chunked"), ("TRANSFER-ENCODING", "chunked"),
], ],
"body": "all your base are belong to us" "body": b"all your base are belong to us"
} }
request = [req1, req2] request = [req1, req2]

View File

@ -1,43 +1,43 @@
# -*- coding: utf-8 - # -*- coding: utf-8 -
# Copyright 2009 Paul J. Davis <paul.joseph.davis@gmail.com> # Copyright 2009 Paul J. Davis <paul.joseph.davis@gmail.com>
# #
# This file is part of gunicorn released under the MIT license. # This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information. # See the NOTICE for more information.
from __future__ import with_statement from __future__ import with_statement
import array import array
import os import os
from StringIO import StringIO
import tempfile import tempfile
dirname = os.path.dirname(__file__) dirname = os.path.dirname(__file__)
from gunicorn.http.parser import RequestParser from gunicorn.http.parser import RequestParser
from gunicorn.config import Config from gunicorn.config import Config
from gunicorn.six import BytesIO
def data_source(fname): def data_source(fname):
buf = StringIO() buf = BytesIO()
with open(fname) as handle: with open(fname) as handle:
for line in handle: for line in handle:
line = line.rstrip("\n").replace("\\r\\n", "\r\n") line = line.rstrip("\n").replace("\\r\\n", "\r\n")
buf.write(line) buf.write(line.encode('latin1'))
return buf return buf
class request(object): class request(object):
def __init__(self, name): def __init__(self, name):
self.fname = os.path.join(dirname, "requests", name) self.fname = os.path.join(dirname, "requests", name)
def __call__(self, func): def __call__(self, func):
def run(): def run():
src = data_source(self.fname) src = data_source(self.fname)
func(src, RequestParser(src)) func(src, RequestParser(src))
run.func_name = func.func_name run.func_name = func.func_name
return run return run
class FakeSocket(object): class FakeSocket(object):
def __init__(self, data): def __init__(self, data):
self.tmp = tempfile.TemporaryFile() self.tmp = tempfile.TemporaryFile()
if data: if data:
@ -47,32 +47,32 @@ class FakeSocket(object):
def fileno(self): def fileno(self):
return self.tmp.fileno() return self.tmp.fileno()
def len(self): def len(self):
return self.tmp.len return self.tmp.len
def recv(self, length=None): def recv(self, length=None):
return self.tmp.read() return self.tmp.read()
def recv_into(self, buf, length): def recv_into(self, buf, length):
tmp_buffer = self.tmp.read(length) tmp_buffer = self.tmp.read(length)
v = len(tmp_buffer) v = len(tmp_buffer)
for i, c in enumerate(tmp_buffer): for i, c in enumerate(tmp_buffer):
buf[i] = c buf[i] = c
return v return v
def send(self, data): def send(self, data):
self.tmp.write(data) self.tmp.write(data)
self.tmp.flush() self.tmp.flush()
def seek(self, offset, whence=0): def seek(self, offset, whence=0):
self.tmp.seek(offset, whence) self.tmp.seek(offset, whence)
class http_request(object): class http_request(object):
def __init__(self, name): def __init__(self, name):
self.fname = os.path.join(dirname, "requests", name) self.fname = os.path.join(dirname, "requests", name)
def __call__(self, func): def __call__(self, func):
def run(): def run():
fsock = FakeSocket(data_source(self.fname)) fsock = FakeSocket(data_source(self.fname))
@ -80,7 +80,7 @@ class http_request(object):
func(req) func(req)
run.func_name = func.func_name run.func_name = func.func_name
return run return run
def eq(a, b): def eq(a, b):
assert a == b, "%r != %r" % (a, b) assert a == b, "%r != %r" % (a, b)
@ -117,4 +117,3 @@ def raises(exctype, func, *args, **kwargs):
func_name = getattr(func, "func_name", "<builtin_function>") func_name = getattr(func, "func_name", "<builtin_function>")
raise AssertionError("Function %s did not raise %s" % ( raise AssertionError("Function %s did not raise %s" % (
func_name, exctype.__name__)) func_name, exctype.__name__))

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 - # -*- coding: utf-8 -
# #
# This file is part of gunicorn released under the MIT license. # This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information. # See the NOTICE for more information.
import t import t
@ -9,6 +9,8 @@ import treq
import glob import glob
import os import os
dirname = os.path.dirname(__file__) dirname = os.path.dirname(__file__)
from py.test import skip
reqdir = os.path.join(dirname, "requests", "valid") reqdir = os.path.join(dirname, "requests", "valid")
def a_case(fname): def a_case(fname):

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 - # -*- coding: utf-8 -
# #
# This file is part of gunicorn released under the MIT license. # This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information. # See the NOTICE for more information.
import t import t
@ -8,7 +8,8 @@ import treq
import glob import glob
import os import os
from nose.tools import raises
import pytest
dirname = os.path.dirname(__file__) dirname = os.path.dirname(__file__)
reqdir = os.path.join(dirname, "requests", "invalid") reqdir = os.path.join(dirname, "requests", "invalid")
@ -17,12 +18,12 @@ reqdir = os.path.join(dirname, "requests", "invalid")
def test_http_parser(): def test_http_parser():
for fname in glob.glob(os.path.join(reqdir, "*.http")): for fname in glob.glob(os.path.join(reqdir, "*.http")):
env = treq.load_py(os.path.splitext(fname)[0] + ".py") env = treq.load_py(os.path.splitext(fname)[0] + ".py")
expect = env['request'] expect = env['request']
cfg = env['cfg'] cfg = env['cfg']
req = treq.badrequest(fname) req = treq.badrequest(fname)
@raises(expect) with pytest.raises(expect):
def check(fname): def f(fname):
return req.check(cfg) return req.check(cfg)
f(fname)
yield check, fname # fname is pass so that we know which test failed

View File

@ -1,12 +1,10 @@
# -*- coding: utf-8 - # -*- coding: utf-8 -
# #
# This file is part of gunicorn released under the MIT license. # This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information. # See the NOTICE for more information.
from __future__ import with_statement from __future__ import with_statement
from nose.plugins.skip import SkipTest
import t import t
import functools import functools
@ -23,14 +21,6 @@ def cfg_file():
def paster_ini(): def paster_ini():
return os.path.join(dirname, "..", "examples", "frameworks", "pylonstest", "nose.ini") return os.path.join(dirname, "..", "examples", "frameworks", "pylonstest", "nose.ini")
def PasterApp():
try:
from paste.deploy import loadapp, loadwsgi
except ImportError:
raise SkipTest()
from gunicorn.app.pasterapp import PasterApplication
return PasterApplication("no_usage")
class AltArgs(object): class AltArgs(object):
def __init__(self, args=None): def __init__(self, args=None):
self.args = args or [] self.args = args or []
@ -38,17 +28,17 @@ class AltArgs(object):
def __enter__(self): def __enter__(self):
sys.argv = self.args sys.argv = self.args
def __exit__(self, exc_type, exc_inst, traceback): def __exit__(self, exc_type, exc_inst, traceback):
sys.argv = self.orig sys.argv = self.orig
class NoConfigApp(Application): class NoConfigApp(Application):
def __init__(self): def __init__(self):
super(NoConfigApp, self).__init__("no_usage") super(NoConfigApp, self).__init__("no_usage")
def init(self, parser, opts, args): def init(self, parser, opts, args):
pass pass
def load(self): def load(self):
pass pass
@ -63,25 +53,25 @@ def test_property_access():
c = config.Config() c = config.Config()
for s in config.KNOWN_SETTINGS: for s in config.KNOWN_SETTINGS:
getattr(c, s.name) getattr(c, s.name)
# Class was loaded # Class was loaded
t.eq(c.worker_class, SyncWorker) t.eq(c.worker_class, SyncWorker)
# Debug affects workers # Debug affects workers
t.eq(c.workers, 1) t.eq(c.workers, 1)
c.set("workers", 3) c.set("workers", 3)
t.eq(c.workers, 3) t.eq(c.workers, 3)
# Address is parsed # Address is parsed
t.eq(c.address, ("127.0.0.1", 8000)) t.eq(c.address, ("127.0.0.1", 8000))
# User and group defaults # User and group defaults
t.eq(os.geteuid(), c.uid) t.eq(os.geteuid(), c.uid)
t.eq(os.getegid(), c.gid) t.eq(os.getegid(), c.gid)
# Proc name # Proc name
t.eq("gunicorn", c.proc_name) t.eq("gunicorn", c.proc_name)
# Not a config property # Not a config property
t.raises(AttributeError, getattr, c, "foo") t.raises(AttributeError, getattr, c, "foo")
# Force to be not an error # Force to be not an error
@ -93,10 +83,10 @@ def test_property_access():
# Attempt to set a cfg not via c.set # Attempt to set a cfg not via c.set
t.raises(AttributeError, setattr, c, "proc_name", "baz") t.raises(AttributeError, setattr, c, "proc_name", "baz")
# No setting for name # No setting for name
t.raises(AttributeError, c.set, "baz", "bar") t.raises(AttributeError, c.set, "baz", "bar")
def test_bool_validation(): def test_bool_validation():
c = config.Config() c = config.Config()
t.eq(c.debug, False) t.eq(c.debug, False)
@ -196,30 +186,9 @@ def test_load_config():
t.eq(app.cfg.bind, "unix:/tmp/bar/baz") t.eq(app.cfg.bind, "unix:/tmp/bar/baz")
t.eq(app.cfg.workers, 3) t.eq(app.cfg.workers, 3)
t.eq(app.cfg.proc_name, "fooey") t.eq(app.cfg.proc_name, "fooey")
def test_cli_overrides_config(): def test_cli_overrides_config():
with AltArgs(["prog_name", "-c", cfg_file(), "-b", "blarney"]): with AltArgs(["prog_name", "-c", cfg_file(), "-b", "blarney"]):
app = NoConfigApp() app = NoConfigApp()
t.eq(app.cfg.bind, "blarney") t.eq(app.cfg.bind, "blarney")
t.eq(app.cfg.proc_name, "fooey") t.eq(app.cfg.proc_name, "fooey")
def test_paster_config():
with AltArgs(["prog_name", paster_ini()]):
app = PasterApp()
t.eq(app.cfg.bind, "192.168.0.1:80")
t.eq(app.cfg.proc_name, "brim")
t.eq("ignore_me" in app.cfg.settings, False)
def test_cfg_over_paster():
with AltArgs(["prog_name", "-c", cfg_file(), paster_ini()]):
app = PasterApp()
t.eq(app.cfg.bind, "unix:/tmp/bar/baz")
t.eq(app.cfg.proc_name, "fooey")
t.eq(app.cfg.default_proc_name, "blurgh")
def test_cli_cfg_paster():
with AltArgs(["prog_name", "-c", cfg_file(), "-b", "whee", paster_ini()]):
app = PasterApp()
t.eq(app.cfg.bind, "whee")
t.eq(app.cfg.proc_name, "fooey")
t.eq(app.cfg.default_proc_name, "blurgh")

View File

@ -0,0 +1,61 @@
import t
from gunicorn.http.body import Body
from gunicorn.six import BytesIO
def assert_readline(payload, size, expected):
body = Body(BytesIO(payload))
t.eq(body.readline(size), expected)
def test_readline_empty_body():
assert_readline(b"", None, b"")
assert_readline(b"", 1, b"")
def test_readline_zero_size():
assert_readline(b"abc", 0, b"")
assert_readline(b"\n", 0, b"")
def test_readline_new_line_before_size():
body = Body(BytesIO(b"abc\ndef"))
t.eq(body.readline(4), b"abc\n")
t.eq(body.readline(), b"def")
def test_readline_new_line_after_size():
body = Body(BytesIO(b"abc\ndef"))
t.eq(body.readline(2), b"ab")
t.eq(body.readline(), b"c\n")
def test_readline_no_new_line():
body = Body(BytesIO(b"abcdef"))
t.eq(body.readline(), b"abcdef")
body = Body(BytesIO(b"abcdef"))
t.eq(body.readline(2), b"ab")
t.eq(body.readline(2), b"cd")
t.eq(body.readline(2), b"ef")
def test_readline_buffer_loaded():
reader = BytesIO(b"abc\ndef")
body = Body(reader)
body.read(1) # load internal buffer
reader.write(b"g\nhi")
reader.seek(7)
print(reader.getvalue())
t.eq(body.readline(), b"bc\n")
t.eq(body.readline(), b"defg\n")
t.eq(body.readline(), b"hi")
def test_readline_buffer_loaded_with_size():
body = Body(BytesIO(b"abc\ndef"))
body.read(1) # load internal buffer
t.eq(body.readline(2), b"bc")
t.eq(body.readline(2), b"\n")
t.eq(body.readline(2), b"de")
t.eq(body.readline(2), b"f")

View File

@ -10,18 +10,19 @@ import t
import inspect import inspect
import os import os
import random import random
import urlparse
from gunicorn.config import Config from gunicorn.config import Config
from gunicorn.http.errors import ParseException from gunicorn.http.errors import ParseException
from gunicorn.http.parser import RequestParser from gunicorn.http.parser import RequestParser
from gunicorn.six import urlparse, execfile_
from gunicorn import six
dirname = os.path.dirname(__file__) dirname = os.path.dirname(__file__)
random.seed() random.seed()
def uri(data): def uri(data):
ret = {"raw": data} ret = {"raw": data}
parts = urlparse.urlparse(data) parts = urlparse(data)
ret["scheme"] = parts.scheme or '' ret["scheme"] = parts.scheme or ''
ret["host"] = parts.netloc.rsplit(":", 1)[0] or None ret["host"] = parts.netloc.rsplit(":", 1)[0] or None
ret["port"] = parts.port or 80 ret["port"] = parts.port or 80
@ -42,7 +43,7 @@ def load_py(fname):
config = globals().copy() config = globals().copy()
config["uri"] = uri config["uri"] = uri
config["cfg"] = Config() config["cfg"] = Config()
execfile(fname, config) execfile_(fname, config)
return config return config
class request(object): class request(object):
@ -54,10 +55,10 @@ class request(object):
if not isinstance(self.expect, list): if not isinstance(self.expect, list):
self.expect = [self.expect] self.expect = [self.expect]
with open(self.fname) as handle: with open(self.fname, 'rb') as handle:
self.data = handle.read() self.data = handle.read()
self.data = self.data.replace("\n", "").replace("\\r\\n", "\r\n") self.data = self.data.replace(b"\n", b"").replace(b"\\r\\n", b"\r\n")
self.data = self.data.replace("\\0", "\000") self.data = self.data.replace(b"\\0", b"\000")
# Functions for sending data to the parser. # Functions for sending data to the parser.
# These functions mock out reading from a # These functions mock out reading from a
@ -69,20 +70,20 @@ class request(object):
def send_lines(self): def send_lines(self):
lines = self.data lines = self.data
pos = lines.find("\r\n") pos = lines.find(b"\r\n")
while pos > 0: while pos > 0:
yield lines[:pos+2] yield lines[:pos+2]
lines = lines[pos+2:] lines = lines[pos+2:]
pos = lines.find("\r\n") pos = lines.find(b"\r\n")
if len(lines): if len(lines):
yield lines yield lines
def send_bytes(self): def send_bytes(self):
for d in self.data: for d in str(self.data, "latin1"):
yield d yield bytes(d, "latin1")
def send_random(self): def send_random(self):
maxs = len(self.data) / 10 maxs = round(len(self.data) / 10)
read = 0 read = 0
while read < len(self.data): while read < len(self.data):
chunk = random.randint(1, maxs) chunk = random.randint(1, maxs)
@ -143,7 +144,7 @@ class request(object):
while len(body): while len(body):
if body[:len(data)] != data: if body[:len(data)] != data:
raise AssertionError("Invalid data read: %r" % data) raise AssertionError("Invalid data read: %r" % data)
if '\n' in data[:-1]: if b'\n' in data[:-1]:
raise AssertionError("Embedded new line: %r" % data) raise AssertionError("Embedded new line: %r" % data)
body = body[len(data):] body = body[len(data):]
data = self.szread(req.body.readline, sizes) data = self.szread(req.body.readline, sizes)
@ -165,7 +166,7 @@ class request(object):
""" """
data = req.body.readlines() data = req.body.readlines()
for line in data: for line in data:
if '\n' in line[:-1]: if b'\n' in line[:-1]:
raise AssertionError("Embedded new line: %r" % line) raise AssertionError("Embedded new line: %r" % line)
if line != body[:len(line)]: if line != body[:len(line)]:
raise AssertionError("Invalid body data read: %r != %r" % ( raise AssertionError("Invalid body data read: %r != %r" % (
@ -182,7 +183,7 @@ class request(object):
This skips sizes because there's its not part of the iter api. This skips sizes because there's its not part of the iter api.
""" """
for line in req.body: for line in req.body:
if '\n' in line[:-1]: if b'\n' in line[:-1]:
raise AssertionError("Embedded new line: %r" % line) raise AssertionError("Embedded new line: %r" % line)
if line != body[:len(line)]: if line != body[:len(line)]:
raise AssertionError("Invalid body data read: %r != %r" % ( raise AssertionError("Invalid body data read: %r != %r" % (
@ -191,7 +192,7 @@ class request(object):
if len(body): if len(body):
raise AssertionError("Failed to read entire body: %r" % body) raise AssertionError("Failed to read entire body: %r" % body)
try: try:
data = iter(req.body).next() data = six.next(iter(req.body))
raise AssertionError("Read data after body finished: %r" % data) raise AssertionError("Read data after body finished: %r" % data)
except StopIteration: except StopIteration:
pass pass
@ -214,9 +215,15 @@ class request(object):
ret = [] ret = []
for (mt, sz, sn) in cfgs: for (mt, sz, sn) in cfgs:
mtn = mt.func_name[6:] if hasattr(mt, 'funcname'):
szn = sz.func_name[5:] mtn = mt.func_name[6:]
snn = sn.func_name[5:] szn = sz.func_name[5:]
snn = sn.func_name[5:]
else:
mtn = mt.__name__[6:]
szn = sz.__name__[5:]
snn = sn.__name__[5:]
def test_req(sn, sz, mt): def test_req(sn, sz, mt):
self.check(cfg, sn, sz, mt) self.check(cfg, sn, sz, mt)
desc = "%s: MT: %s SZ: %s SN: %s" % (self.name, mtn, szn, snn) desc = "%s: MT: %s SZ: %s SN: %s" % (self.name, mtn, szn, snn)
@ -251,9 +258,10 @@ class badrequest(object):
self.data = handle.read() self.data = handle.read()
self.data = self.data.replace("\n", "").replace("\\r\\n", "\r\n") self.data = self.data.replace("\n", "").replace("\\r\\n", "\r\n")
self.data = self.data.replace("\\0", "\000") self.data = self.data.replace("\\0", "\000")
self.data = self.data.encode('latin1')
def send(self): def send(self):
maxs = len(self.data) / 10 maxs = round(len(self.data) / 10)
read = 0 read = 0
while read < len(self.data): while read < len(self.data):
chunk = random.randint(1, maxs) chunk = random.randint(1, maxs)
@ -262,5 +270,4 @@ class badrequest(object):
def check(self, cfg): def check(self, cfg):
p = RequestParser(cfg, self.send()) p = RequestParser(cfg, self.send())
[req for req in p] six.next(p)