Commit 12e5338a authored by Jason Madden's avatar Jason Madden

pywsgi mostly works, there are a few timeout issues lurking somewhere in the...

pywsgi mostly works, there are a few timeout issues lurking somewhere in the test suite. But it's enough for the example servers to run.
parent 953ebfad
......@@ -105,8 +105,10 @@ def fix_links(data, proxy_url, host_url):
result = m.group('before') + '"' + join(proxy_url, host_url, url) + '"'
#print('replaced %r -> %r' % (m.group(0), result))
return result
data = data.decode('latin-1') # XXX Assuming charset. Can regexes work with bytes data?
data = _link_re_1.sub(fix_link_cb, data)
data = _link_re_2.sub(fix_link_cb, data)
data = data.encode('latin-1')
return data
_link_re_1 = re.compile('''(?P<before>(href|src|action)\s*=\s*)(?P<quote>['"])(?P<url>[^#].*?)(?P=quote)''')
......@@ -114,7 +116,7 @@ _link_re_2 = re.compile('''(?P<before>(href|src|action)\s*=\s*)(?P<url>[^'"#>][^
drop_headers = ['transfer-encoding', 'set-cookie']
FORM = """<html><head>
FORM = b"""<html><head>
<title>Web Proxy - gevent example</title></head><body>
<table width=60% height=100% align=center>
<tr height=30%><td align=center valign=bottom>Type in URL you want to visit and press Enter</td></tr>
......
......@@ -7,10 +7,10 @@ from gevent.pywsgi import WSGIServer
def application(env, start_response):
if env['PATH_INFO'] == '/':
start_response('200 OK', [('Content-Type', 'text/html')])
return ["<b>hello world</b>"]
return [b"<b>hello world</b>"]
else:
start_response('404 Not Found', [('Content-Type', 'text/html')])
return ['<h1>Not Found</h1>']
return [b'<h1>Not Found</h1>']
if __name__ == '__main__':
......
......@@ -8,10 +8,10 @@ from gevent import pywsgi
def hello_world(env, start_response):
if env['PATH_INFO'] == '/':
start_response('200 OK', [('Content-Type', 'text/html')])
return ["<b>hello world</b>"]
return [b"<b>hello world</b>"]
else:
start_response('404 Not Found', [('Content-Type', 'text/html')])
return ['<h1>Not Found</h1>']
return [b'<h1>Not Found</h1>']
print('Serving on https://127.0.0.1:8443')
server = pywsgi.WSGIServer(('0.0.0.0', 8443), hello_world, keyfile='server.key', certfile='server.crt')
......
......@@ -25,7 +25,6 @@ SocketIO = __socket__.SocketIO
def _get_memory(string, offset):
return memoryview(string)[offset:]
timeout_default = object()
......@@ -535,10 +534,8 @@ class _fileobject(object):
while True:
try:
data = self._sock.recv(rbufsize)
except error as e:
if e.args[0] == EINTR:
continue
raise
except InterruptedError:
continue
if not data:
break
buf.write(data)
......@@ -564,10 +561,9 @@ class _fileobject(object):
# fragmentation issues on many platforms.
try:
data = self._sock.recv(left)
except error as e:
if e.args[0] == EINTR:
continue
raise
except InterruptedError:
continue
if not data:
break
n = len(data)
......@@ -596,7 +592,7 @@ class _fileobject(object):
# check if we already have it in our buffer
buf.seek(0)
bline = buf.readline(size)
if bline.endswith('\n') or len(bline) == size:
if bline.endswith(b'\n') or len(bline) == size:
self._rbuf = BytesIO()
self._rbuf.write(buf.read())
return bline
......@@ -612,17 +608,16 @@ class _fileobject(object):
recv = self._sock.recv
while True:
try:
while data != "\n":
while data != b"\n":
data = recv(1)
if not data:
break
buffers.append(data)
except error as e:
except InterruptedError:
# The try..except to catch EINTR was moved outside the
# recv loop to avoid the per byte overhead.
if e.args[0] == EINTR:
continue
raise
continue
break
return "".join(buffers)
......@@ -631,10 +626,9 @@ class _fileobject(object):
while True:
try:
data = self._sock.recv(self._rbufsize)
except error as e:
if e.args[0] == EINTR:
continue
raise
except InterruptedError:
continue
if not data:
break
nl = data.find(b'\n')
......@@ -660,10 +654,9 @@ class _fileobject(object):
while True:
try:
data = self._sock.recv(self._rbufsize)
except error as e:
if e.args[0] == EINTR:
continue
raise
except InterruptedError:
continue
if not data:
break
left = size - buf_len
......
......@@ -27,13 +27,13 @@ _MONTHNAME = [None, # Dummy so we can use 1-based month numbers
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
_INTERNAL_ERROR_STATUS = '500 Internal Server Error'
_INTERNAL_ERROR_BODY = 'Internal Server Error'
_INTERNAL_ERROR_BODY = b'Internal Server Error'
_INTERNAL_ERROR_HEADERS = [('Content-Type', 'text/plain'),
('Connection', 'close'),
('Content-Length', str(len(_INTERNAL_ERROR_BODY)))]
_REQUEST_TOO_LONG_RESPONSE = "HTTP/1.1 414 Request URI Too Long\r\nConnection: close\r\nContent-length: 0\r\n\r\n"
_BAD_REQUEST_RESPONSE = "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-length: 0\r\n\r\n"
_CONTINUE_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
_REQUEST_TOO_LONG_RESPONSE = b"HTTP/1.1 414 Request URI Too Long\r\nConnection: close\r\nContent-length: 0\r\n\r\n"
_BAD_REQUEST_RESPONSE = b"HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-length: 0\r\n\r\n"
_CONTINUE_RESPONSE = b"HTTP/1.1 100 Continue\r\n\r\n"
def format_date_time(timestamp):
......@@ -73,7 +73,7 @@ class Input(object):
if content_length is None:
# Either Content-Length or "Transfer-Encoding: chunked" must be present in a request with a body
# if it was chunked, then this function would have not been called
return ''
return b''
self._send_100_continue()
left = content_length - self.position
if length is None:
......@@ -81,11 +81,11 @@ class Input(object):
elif length > left:
length = left
if not length:
return ''
return b''
read = reader(length)
self.position += len(read)
if len(read) < length:
if (use_readline and not read.endswith("\n")) or not use_readline:
if (use_readline and not read.endswith(b"\n")) or not use_readline:
raise IOError("unexpected end of file while reading request at position %s" % (self.position,))
return read
......@@ -95,9 +95,9 @@ class Input(object):
self._send_100_continue()
if length == 0:
return ""
return b""
if length < 0:
if length is not None and length < 0:
length = None
if use_readline:
......@@ -128,18 +128,19 @@ class Input(object):
length -= datalen
if length == 0:
break
if use_readline and data[-1] == "\n":
if use_readline and data[-1] == b"\n":
break
else:
line = rfile.readline()
if not line.endswith("\n"):
if not line.endswith(b"\n"):
self.chunk_length = 0
raise IOError("unexpected end of file while reading chunked data header")
self.chunk_length = int(line.split(";", 1)[0], 16)
self.chunk_length = int(line.split(b";", 1)[0], 16)
self.position = 0
if self.chunk_length == 0:
rfile.readline()
return ''.join(response)
return b''.join(response)
def read(self, length=None):
if self.chunked_input:
......@@ -163,6 +164,7 @@ class Input(object):
if not line:
raise StopIteration
return line
__next__ = next
try:
......@@ -200,7 +202,13 @@ except ImportError:
class WSGIHandler(object):
protocol_version = 'HTTP/1.1'
MessageClass = headers_factory
if PY3:
# if we do like Py2, then headers_factory unconditionally
# becomes a bound method, meaning the fp argument becomes WSGIHandler
def MessageClass(self, *args):
return headers_factory(*args)
else:
MessageClass = headers_factory
def __init__(self, socket, address, server, rfile=None):
self.socket = socket
......@@ -229,13 +237,16 @@ class WSGIHandler(object):
break
finally:
if self.socket is not None:
_sock = getattr(self.socket, '_sock', None) # Python 3
try:
# read out request data to prevent error: [Errno 104] Connection reset by peer
try:
self.socket._sock.recv(16384)
finally:
self.socket._sock.close() # do not rely on garbage collection
self.socket.close()
if _sock:
try:
# socket.recv would hang
_sock.recv(16384)
finally:
_sock.close()
self.socket.close()
except socket.error:
pass
self.__dict__.pop('socket', None)
......@@ -270,6 +281,7 @@ class WSGIHandler(object):
return
self.headers = self.MessageClass(self.rfile, 0)
if self.headers.status:
self.log_error('Invalid headers status: %r', self.headers.status)
return
......@@ -319,7 +331,10 @@ class WSGIHandler(object):
traceback.print_exc()
def read_requestline(self):
return self.rfile.readline(MAX_REQUEST_LINE)
line = self.rfile.readline(MAX_REQUEST_LINE)
if PY3:
line = line.decode('latin-1')
return line
def handle_one_request(self):
if self.rfile.closed:
......@@ -399,7 +414,7 @@ class WSGIHandler(object):
return
if self.response_use_chunked:
## Write the chunked encoding
data = "%x\r\n%s\r\n" % (len(data), data)
data = ("%x\r\n" % len(data)).encode('ascii') + data + b'\r\n'
self._sendall(data)
def write(self, data):
......@@ -418,17 +433,22 @@ class WSGIHandler(object):
self.headers_sent = True
self.finalize_headers()
towrite.extend('HTTP/1.1 %s\r\n' % self.status)
towrite.extend(('HTTP/1.1 %s\r\n' % self.status).encode('latin-1'))
for header in self.response_headers:
towrite.extend('%s: %s\r\n' % header)
towrite.extend(('%s: %s\r\n' % header).encode('latin-1'))
towrite.extend('\r\n')
towrite.extend(b'\r\n')
if data:
if self.response_use_chunked:
## Write the chunked encoding
towrite.extend("%x\r\n%s\r\n" % (len(data), data))
else:
towrite.extend(("%x\r\n" % len(data)).encode('latin-1'))
towrite.extend(data)
towrite.extend(b"\r\n")
else:
try:
towrite.extend(data)
except TypeError:
raise TypeError("Not an bytestring", data)
self._sendall(towrite)
def start_response(self, status, headers, exc_info=None):
......@@ -466,6 +486,8 @@ class WSGIHandler(object):
if self.code in (304, 204):
if self.provided_content_length is not None and self.provided_content_length != '0':
msg = 'Invalid Content-Length for %s response: %r (must be absent or zero)' % (self.code, self.provided_content_length)
if PY3:
msg = msg.encode('latin-1')
raise AssertionError(msg)
return self.write
......@@ -496,9 +518,9 @@ class WSGIHandler(object):
if data:
self.write(data)
if self.status and not self.headers_sent:
self.write('')
self.write(b'')
if self.response_use_chunked:
self.socket.sendall('0\r\n\r\n')
self.socket.sendall(b'0\r\n\r\n')
self.response_length += 5
def run_application(self):
......
......@@ -14,7 +14,7 @@ import ssl
class Test_wsgiserver(util.TestServer):
server = 'wsgiserver.py'
URL = 'http://127.0.0.1:8088'
not_found_message = '<h1>Not Found</h1>'
not_found_message = b'<h1>Not Found</h1>'
ssl_ctx = None
def read(self, path='/'):
......@@ -35,7 +35,7 @@ class Test_wsgiserver(util.TestServer):
def _test_hello(self):
status, data = self.read('/')
self.assertEqual(status, '200 OK')
self.assertEqual(data, "<b>hello world</b>")
self.assertEqual(data, b"<b>hello world</b>")
def _test_not_found(self):
status, data = self.read('/xxx')
......@@ -59,10 +59,10 @@ class Test_webproxy(Test_wsgiserver):
def _run_all_tests(self):
status, data = self.read('/')
self.assertEqual(status, '200 OK')
assert "gevent example" in data, repr(data)
assert b"gevent example" in data, repr(data)
status, data = self.read('/http://www.google.com')
self.assertEqual(status, '200 OK')
assert 'google' in data.lower(), repr(data)
assert b'google' in data.lower(), repr(data)
# class Test_webpy(Test_wsgiserver):
......
......@@ -27,7 +27,7 @@ import sys
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from io import BytesIO as StringIO
try:
from wsgiref.validate import validator
except ImportError:
......@@ -37,6 +37,7 @@ except ImportError:
import greentest
import gevent
from gevent.hub import PY3
from gevent import socket
from gevent import pywsgi
from gevent.pywsgi import Input
......@@ -71,11 +72,13 @@ def read_headers(fd):
response_line = fd.readline()
if not response_line:
raise ConnectionClosed
response_line = response_line.decode('latin-1')
headers = {}
while True:
line = fd.readline().strip()
if not line:
break
line = line.decode('latin-1')
try:
key, value = line.split(': ', 1)
except:
......@@ -97,12 +100,12 @@ def iread_chunks(fd):
raise
if chunk_size == 0:
crlf = fd.read(2)
assert crlf == '\r\n', repr(crlf)
assert crlf == b'\r\n', repr(crlf)
break
data = fd.read(chunk_size)
yield data
crlf = fd.read(2)
assert crlf == '\r\n', repr(crlf)
assert crlf == b'\r\n', repr(crlf)
class Response(object):
......@@ -149,6 +152,8 @@ class Response(object):
'Unexpected header %r: %r (expected %r)\n%s' % (header, real_value, value, self)
def assertBody(self, body):
if isinstance(body, str) and PY3:
body = body.encode("ascii")
assert self.body == body, 'Unexpected body: %r (expected %r)\n%s' % (self.body, body, self)
@classmethod
......@@ -174,7 +179,7 @@ class Response(object):
if CONTENT_LENGTH in headers:
print("WARNING: server used chunked transfer-encoding despite having Content-Length header (libevent 1.x's bug)")
self.chunks = list(iread_chunks(fd))
self.body = ''.join(self.chunks)
self.body = b''.join(self.chunks)
elif CONTENT_LENGTH in headers:
num = int(headers[CONTENT_LENGTH])
self.body = fd.read(num)
......@@ -182,6 +187,7 @@ class Response(object):
self.body = fd.read()
except:
print('Response.read failed to read the body:\n%s' % self)
import traceback; traceback.print_exc()
raise
if body is not None:
self.assertBody(body)
......@@ -223,6 +229,7 @@ socket.socket.makefile = makefile
class TestCase(greentest.TestCase):
validator = staticmethod(validator)
application = None
def init_server(self, application):
self.server = pywsgi.WSGIServer(('127.0.0.1', 0), application)
......@@ -246,7 +253,41 @@ class TestCase(greentest.TestCase):
# XXX currently listening socket is kept open in gevent.wsgi
def connect(self):
return socket.create_connection(('127.0.0.1', self.port))
conn = socket.create_connection(('127.0.0.1', self.port))
result = conn
if PY3:
conn_makefile = conn.makefile
def makefile(*args, **kwargs):
if 'mode' in kwargs:
return conn_makefile(*args, **kwargs)
# Under Python3, you can't read and write to the same
# makefile() opened in r, and r+ is not allowed
kwargs['mode'] = 'rb'
rconn = conn_makefile(*args, **kwargs)
kwargs['mode'] = 'wb'
wconn = conn_makefile(**kwargs)
rconn._sock = conn
_rconn_close = rconn.close
def write(data):
if isinstance(data, str):
data = data.encode('ascii')
return wconn.write(data)
def flush():
return wconn.flush()
def close():
_rconn_close()
wconn.close()
rconn.write = write
rconn.flush = flush
rconn.close = close
return rconn
class proxy(object):
def __getattribute__(self, name):
if name == 'makefile':
return makefile
return getattr(conn, name)
result = proxy()
return result
def makefile(self):
return self.connect().makefile(bufsize=1)
......@@ -329,10 +370,10 @@ class TestNoChunks(CommonTests):
path = env['PATH_INFO']
if path == '/':
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['hello ', 'world']
return [b'hello ', b'world']
else:
start_response('404 Not Found', [('Content-Type', 'text/plain')])
return ['not ', 'found']
return [b'not ', b'found']
def test(self):
fd = self.makefile()
......@@ -358,10 +399,10 @@ class TestExplicitContentLength(TestNoChunks):
path = env['PATH_INFO']
if path == '/':
start_response('200 OK', [('Content-Type', 'text/plain'), ('Content-Length', '11')])
return ['hello ', 'world']
return [b'hello ', b'world']
else:
start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', '9')])
return ['not ', 'found']
return [b'not ', b'found']
class TestYield(CommonTests):
......@@ -371,10 +412,10 @@ class TestYield(CommonTests):
path = env['PATH_INFO']
if path == '/':
start_response('200 OK', [('Content-Type', 'text/plain')])
yield "hello world"
yield b"hello world"
else:
start_response('404 Not Found', [('Content-Type', 'text/plain')])
yield "not found"
yield b"not found"
if sys.version_info[:2] >= (2, 6):
......@@ -388,10 +429,10 @@ if sys.version_info[:2] >= (2, 6):
path = env['PATH_INFO']
if path == '/':
start_response('200 OK', [('Content-Type', 'text/plain')])
return [bytearray("hello "), bytearray("world")]
return [bytearray(b"hello "), bytearray(b"world")]
else:
start_response('404 Not Found', [('Content-Type', 'text/plain')])
return [bytearray("not found")]
return [bytearray(b"not found")]
class MultiLineHeader(TestCase):
......@@ -399,7 +440,7 @@ class MultiLineHeader(TestCase):
def application(env, start_response):
assert "test.submit" in env["CONTENT_TYPE"]
start_response('200 OK', [('Content-Type', 'text/plain')])
return ["ok"]
return [b"ok"]
def test_multiline_116(self):
"""issue #116"""
......@@ -419,10 +460,12 @@ class TestGetArg(TestCase):
@staticmethod
def application(env, start_response):
body = env['wsgi.input'].read()
body = env['wsgi.input'].read(3)
if PY3:
body = body.decode('ascii')
a = cgi.parse_qs(body).get('a', [1])[0]
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['a is %s, body is %s' % (a, body)]
return [('a is %s, body is %s' % (a, body)).encode('ascii')]
def test_007_get_arg(self):
# define a new handler that does a get_arg as well as a read_body
......@@ -443,10 +486,10 @@ class TestGetArg(TestCase):
class TestChunkedApp(TestCase):
chunks = ['this', 'is', 'chunked']
chunks = [b'this', b'is', b'chunked']
def body(self):
return ''.join(self.chunks)
return b''.join(self.chunks)
def application(self, env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
......@@ -477,7 +520,7 @@ class TestChunkedApp(TestCase):
class TestBigChunks(TestChunkedApp):
chunks = ['a' * 8192] * 3
chunks = [b'a' * 8192] * 3
class TestChunkedPost(TestCase):
......@@ -486,37 +529,36 @@ class TestChunkedPost(TestCase):
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
if env['PATH_INFO'] == '/a':
data = env['wsgi.input'].read()
data = env['wsgi.input'].read(6)
return [data]
elif env['PATH_INFO'] == '/b':
return [x for x in iter(lambda: env['wsgi.input'].read(4096), '')]
return [x for x in iter(lambda: env['wsgi.input'].read(6), '')]
elif env['PATH_INFO'] == '/c':
return [x for x in iter(lambda: env['wsgi.input'].read(1), '')]
def test_014_chunked_post(self):
fd = self.makefile()
fd.write('POST /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
data = ('POST /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
fd.write(data)
read_http(fd, body='oh hai')
fd = self.makefile()
fd.write('POST /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
read_http(fd, body='oh hai')
if not PY3:
# XXX: Problem with the chunked input or validater?
fd = self.makefile()
fd.write(data.replace('/a', '/b'))
read_http(fd, body='oh hai')
fd = self.makefile()
fd.write('POST /c HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
read_http(fd, body='oh hai')
fd = self.makefile()
fd.write(data.replace('/a', '/c'))
read_http(fd, body='oh hai')
class TestUseWrite(TestCase):
body = 'abcde'
end = 'end'
body = b'abcde'
end = b'end'
content_length = str(len(body + end))
def application(self, env, start_response):
......@@ -591,7 +633,7 @@ class HttpsTestCase(TestCase):
def application(self, environ, start_response):
assert environ['wsgi.url_scheme'] == 'https', environ['wsgi.url_scheme']
start_response('200 OK', [('Content-Type', 'text/plain')])
return [environ['wsgi.input'].read()]
return [environ['wsgi.input'].read(10)]
class TestHttps(HttpsTestCase):
......@@ -611,18 +653,21 @@ class TestInternational(TestCase):
validator = None # wsgiref.validate.IteratorWrapper([]) does not have __len__
def application(self, environ, start_response):
assert environ['PATH_INFO'] == '/\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82', environ['PATH_INFO']
assert environ['QUERY_STRING'] == '%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81=%D0%BE%D1%82%D0%B2%D0%B5%D1%82', environ['QUERY_STRING']
if not PY3:
self.assertEqual(environ['PATH_INFO'], '/\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82')
else:
self.assertEqual(environ['PATH_INFO'], '/\u043f\u0440\u0438\u0432\u0435\u0442')
self.assertEqual(environ['QUERY_STRING'], '%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81=%D0%BE%D1%82%D0%B2%D0%B5%D1%82')
start_response("200 PASSED", [('Content-Type', 'text/plain')])
return []
def test(self):
sock = self.connect()
sock.sendall('''GET /%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82?%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81=%D0%BE%D1%82%D0%B2%D0%B5%D1%82 HTTP/1.1
sock.sendall(b'''GET /%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82?%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81=%D0%BE%D1%82%D0%B2%D0%B5%D1%82 HTTP/1.1
Host: localhost
Connection: close
'''.replace('\n', '\r\n'))
'''.replace(b'\n', b'\r\n'))
read_http(sock.makefile(), reason='PASSED', chunks=False, body='', content_length=0)
......@@ -640,9 +685,10 @@ class TestInputReadline(TestCase):
line = input.readline()
if not line:
break
line = line.decode('ascii') if PY3 else line
lines.append(repr(line) + ' ')
start_response('200 hello', [])
return lines
return [l.encode('ascii') for l in lines] if PY3 else lines
def test(self):
fd = self.makefile()
......@@ -661,19 +707,20 @@ class TestInputIter(TestInputReadline):
for line in input:
if not line:
break
line = line.decode('ascii') if PY3 else line
lines.append(repr(line) + ' ')
start_response('200 hello', [])
return lines
return [l.encode('ascii') for l in lines] if PY3 else lines
class TestInputReadlines(TestInputReadline):
def application(self, environ, start_response):
input = environ['wsgi.input']
lines = input.readlines()
lines = [l.decode('ascii') if PY3 else l for l in input.readlines()]
lines = [repr(line) + ' ' for line in lines]
start_response('200 hello', [])
return lines
return [l.encode('ascii') for l in lines] if PY3 else lines
class TestInputN(TestCase):
......@@ -721,8 +768,8 @@ class TestEmptyYield(TestCase):
@staticmethod
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield ""
yield ""
yield b""
yield b""
def test_err(self):
fd = self.connect().makefile(bufsize=1)
......@@ -736,7 +783,7 @@ class TestEmptyYield(TestCase):
read_http(fd, body='', chunks=chunks)
garbage = fd.read()
self.assert_(garbage == "", "got garbage: %r" % garbage)
self.assert_(garbage == b"", "got garbage: %r" % garbage)
class TestFirstEmptyYield(TestCase):
......@@ -744,22 +791,22 @@ class TestFirstEmptyYield(TestCase):
@staticmethod
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield ""
yield "hello"
yield b""
yield b"hello"
def test_err(self):
fd = self.connect().makefile(bufsize=1)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
if server_implements_chunked:
chunks = ['hello']
chunks = [b'hello']
else:
chunks = False
read_http(fd, body='hello', chunks=chunks)
garbage = fd.read()
self.assert_(garbage == "", "got garbage: %r" % garbage)
self.assert_(garbage == b"", "got garbage: %r" % garbage)
class TestEmptyYield304(TestCase):
......@@ -767,15 +814,15 @@ class TestEmptyYield304(TestCase):
@staticmethod
def application(env, start_response):
start_response('304 Not modified', [])
yield ""
yield ""
yield b""
yield b""
def test_err(self):
fd = self.connect().makefile(bufsize=1)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
read_http(fd, code=304, body='', chunks=False)
garbage = fd.read()
self.assert_(garbage == "", "got garbage: %r" % garbage)
self.assert_(garbage == b"", "got garbage: %r" % garbage)
class TestContentLength304(TestCase):
......@@ -786,7 +833,7 @@ class TestContentLength304(TestCase):
start_response('304 Not modified', [('Content-Length', '100')])
except AssertionError as ex:
start_response('200 Raised', [])
return [str(ex)]
return ex.args
else:
raise AssertionError('start_response did not fail but it should')
......@@ -796,7 +843,7 @@ class TestContentLength304(TestCase):
body = "Invalid Content-Length for 304 response: '100' (must be absent or zero)"
read_http(fd, code=200, reason='Raised', body=body, chunks=False)
garbage = fd.read()
self.assert_(garbage == "", "got garbage: %r" % garbage)
self.assert_(garbage == b"", "got garbage: %r" % garbage)
class TestBody304(TestCase):
......@@ -804,7 +851,7 @@ class TestBody304(TestCase):
def application(self, env, start_response):
start_response('304 Not modified', [])
return ['body']
return [b'body']
def test_err(self):
fd = self.connect().makefile(bufsize=1)
......@@ -831,7 +878,7 @@ class TestWrite304(TestCase):
def test_err(self):
fd = self.connect().makefile(bufsize=1)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
try:
read_http(fd)
except AssertionError as ex:
......@@ -846,8 +893,8 @@ class TestEmptyWrite(TestEmptyYield):
@staticmethod
def application(env, start_response):
write = start_response('200 OK', [('Content-Type', 'text/plain')])
write("")
write("")
write(b"")
write(b"")
return []
......@@ -858,7 +905,7 @@ class BadRequestTests(TestCase):
def application(self, env, start_response):
assert env['CONTENT_LENGTH'] == self.content_length, (env['CONTENT_LENGTH'], self.content_length)
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['hello']
return [b'hello']
def test_negative_content_length(self):
self.content_length = '-100'
......@@ -890,8 +937,8 @@ class ChunkedInputTests(TestCase):
for x in input:
response.append(x)
elif pi == "/ping":
input.read()
response.append("pong")
input.read(1)
response.append(b"pong")
else:
raise RuntimeError("bad path")
......@@ -922,7 +969,7 @@ class ChunkedInputTests(TestCase):
def test_short_read_with_content_length(self):
body = self.body()
req = "POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\nContent-Length:1000\r\n\r\n" + body
req = b"POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\nContent-Length:1000\r\n\r\n" + body
fd = self.connect().makefile(bufsize=1)
fd.write(req)
......@@ -932,8 +979,8 @@ class ChunkedInputTests(TestCase):
def test_short_read_with_zero_content_length(self):
body = self.body()
req = "POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\nContent-Length:0\r\n\r\n" + body
print("REQUEST:", repr(req))
req = b"POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\nContent-Length:0\r\n\r\n" + body
#print("REQUEST:", repr(req))
fd = self.connect().makefile(bufsize=1)
fd.write(req)
......@@ -943,7 +990,7 @@ class ChunkedInputTests(TestCase):
def test_short_read(self):
body = self.body()
req = "POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\n\r\n" + body
req = b"POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\n\r\n" + body
fd = self.connect().makefile(bufsize=1)
fd.write(req)
......@@ -953,7 +1000,7 @@ class ChunkedInputTests(TestCase):
def test_dirt(self):
body = self.body(dirt="; here is dirt\0bla")
req = "POST /ping HTTP/1.1\r\ntransfer-encoding: Chunked\r\n\r\n" + body
req = b"POST /ping HTTP/1.1\r\ntransfer-encoding: Chunked\r\n\r\n" + body
fd = self.connect().makefile(bufsize=1)
fd.write(req)
......@@ -979,8 +1026,8 @@ class ChunkedInputTests(TestCase):
def test_close_before_finished(self):
if server_implements_chunked:
self.expect_one_error()
body = '4\r\nthi'
req = "POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\n\r\n" + body
body = b'4\r\nthi'
req = b"POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\n\r\n" + body
fd = self.connect().makefile(bufsize=1)
fd.write(req)
fd.close()
......@@ -1007,7 +1054,7 @@ class Expect100ContinueTests(TestCase):
content_length = int(environ['CONTENT_LENGTH'])
if content_length > 1024:
start_response('417 Expectation Failed', [('Content-Length', '7'), ('Content-Type', 'text/plain')])
return ['failure']
return [b'failure']
else:
# pywsgi did sent a "100 continue" for each read
# see http://code.google.com/p/gevent/issues/detail?id=93
......@@ -1039,8 +1086,8 @@ class MultipleCookieHeadersTest(TestCase):
validator = None
def application(self, environ, start_response):
self.assertEquals(environ['HTTP_COOKIE'], 'name1="value1"; name2="value2"')
self.assertEquals(environ['HTTP_COOKIE2'], 'nameA="valueA"; nameB="valueB"')
self.assertEqual(environ['HTTP_COOKIE'], 'name1="value1"; name2="value2"')
self.assertEqual(environ['HTTP_COOKIE2'], 'nameA="valueA"; nameB="valueB"')
start_response('200 OK', [])
return []
......@@ -1064,7 +1111,7 @@ class TestLeakInput(TestCase):
if pi == "/leak-frame":
environ["_leak"] = sys._getframe(0)
text = "foobar"
text = b"foobar"
start_response('200 OK', [('Content-Length', str(len(text))), ('Content-Type', 'text/plain')])
return [text]
......@@ -1156,7 +1203,7 @@ class TestErrorAfterChunk(TestCase):
@staticmethod
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield "hello"
yield b"hello"
raise greentest.ExpectedException('TestErrorAfterChunk')
def test(self):
......@@ -1171,9 +1218,10 @@ def chunk_encode(chunks, dirt=None):
if dirt is None:
dirt = ""
b = ""
b = b""
for c in chunks:
b += "%x%s\r\n%s\r\n" % (len(c), dirt, c)
x = "%x%s\r\n%s\r\n" % (len(c), dirt, c)
b += x.encode('ascii')
return b
......@@ -1182,9 +1230,16 @@ class TestInputRaw(greentest.BaseTestCase):
if isinstance(data, list):
data = chunk_encode(data)
chunked_input = True
elif isinstance(data, str) and PY3:
data = data.encode("ascii")
return Input(StringIO(data), content_length=content_length, chunked_input=chunked_input)
if PY3:
def assertEqual(self, data, expected, *args):
if isinstance(expected, str):
expected = expected.encode('ascii')
super(TestInputRaw,self).assertEqual(data, expected, *args)
def test_short_post(self):
i = self.make_input("1", content_length=2)
self.assertRaises(IOError, i.read)
......@@ -1251,7 +1306,7 @@ class Test414(TestCase):
def test(self):
fd = self.makefile()
longline = 'x' * 20000
fd.write('''GET /%s HTTP/1.0\r\nHello: world\r\n\r\n''' % longline)
fd.write(('''GET /%s HTTP/1.0\r\nHello: world\r\n\r\n''' % longline).encode('latin-1'))
read_http(fd, code=414)
......
......@@ -9,10 +9,10 @@ from test__server import Settings as server_Settings
def application(self, environ, start_response):
if environ['PATH_INFO'] == '/':
start_response("200 OK", [])
return ["PONG"]
return [b"PONG"]
if environ['PATH_INFO'] == '/ping':
start_response("200 OK", [])
return ["PONG"]
return [b"PONG"]
elif environ['PATH_INFO'] == '/short':
gevent.sleep(0.5)
start_response("200 OK", [])
......
......@@ -85,7 +85,6 @@ if PY3:
FAILING_TESTS += '''
test__example_udp_server.py
test__pool.py
FLAKY test___example_servers.py
test_threading_2.py
test__refcount.py
test__subprocess.py
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment