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):
......
This diff is collapsed.
......@@ -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