|
|
@@ -102,17 +102,23 @@ number of requests and their responses, so we run a nested loop:
|
|
|
"""
|
|
|
|
|
|
|
|
|
+from future import standard_library
|
|
|
+standard_library.install_aliases()
|
|
|
+from builtins import hex
|
|
|
+from builtins import range
|
|
|
+from past.builtins import basestring
|
|
|
+from builtins import object
|
|
|
import base64
|
|
|
import os
|
|
|
-import Queue
|
|
|
+import queue
|
|
|
import re
|
|
|
quoted_slash = re.compile("(?i)%2F")
|
|
|
import rfc822
|
|
|
import socket
|
|
|
try:
|
|
|
- import cStringIO as StringIO
|
|
|
+ import io as StringIO
|
|
|
except ImportError:
|
|
|
- import StringIO
|
|
|
+ import io
|
|
|
|
|
|
_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
|
|
|
|
|
|
@@ -120,8 +126,6 @@ import sys
|
|
|
import threading
|
|
|
import time
|
|
|
import traceback
|
|
|
-from urllib import unquote
|
|
|
-from urlparse import urlparse
|
|
|
import warnings
|
|
|
|
|
|
try:
|
|
|
@@ -133,6 +137,15 @@ except ImportError:
|
|
|
import errno
|
|
|
import logging
|
|
|
|
|
|
+if sys.version_info[0] > 2:
|
|
|
+ from io import StringIO as string_io
|
|
|
+ import urllib.request, urllib.error
|
|
|
+ from urllib.parse import unquote as urllib_unquote, urlparse as lib_urlparse
|
|
|
+else:
|
|
|
+ from cStringIO import StringIO as string_io
|
|
|
+ from urllib import unquote as urllib_unquote
|
|
|
+ from urlparse import urlparse as lib_urlparse
|
|
|
+
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def plat_specific_errors(*errnames):
|
|
|
@@ -145,7 +158,7 @@ def plat_specific_errors(*errnames):
|
|
|
errno_names = dir(errno)
|
|
|
nums = [getattr(errno, k) for k in errnames if k in errno_names]
|
|
|
# de-dupe the list
|
|
|
- return dict.fromkeys(nums).keys()
|
|
|
+ return list(dict.fromkeys(nums).keys())
|
|
|
|
|
|
socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
|
|
|
|
|
|
@@ -181,7 +194,7 @@ class WSGIPathInfoDispatcher(object):
|
|
|
|
|
|
def __init__(self, apps):
|
|
|
try:
|
|
|
- apps = apps.items()
|
|
|
+ apps = list(apps.items())
|
|
|
except AttributeError:
|
|
|
pass
|
|
|
|
|
|
@@ -267,8 +280,8 @@ class SizeCheckWrapper(object):
|
|
|
def __iter__(self):
|
|
|
return self
|
|
|
|
|
|
- def next(self):
|
|
|
- data = self.rfile.next()
|
|
|
+ def __next__(self):
|
|
|
+ data = next(self.rfile)
|
|
|
self.bytes_read += len(data)
|
|
|
self._check_length()
|
|
|
return data
|
|
|
@@ -376,7 +389,7 @@ class HTTPRequest(object):
|
|
|
environ["REQUEST_METHOD"] = method
|
|
|
|
|
|
# path may be an abs_path (including "http://host.domain.tld");
|
|
|
- scheme, location, path, params, qs, frag = urlparse(path)
|
|
|
+ scheme, location, path, params, qs, frag = lib_urlparse(path)
|
|
|
|
|
|
if frag:
|
|
|
self.simple_response("400 Bad Request",
|
|
|
@@ -396,7 +409,7 @@ class HTTPRequest(object):
|
|
|
# But note that "...a URI must be separated into its components
|
|
|
# before the escaped characters within those components can be
|
|
|
# safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
|
|
|
- atoms = [unquote(x) for x in quoted_slash.split(path)]
|
|
|
+ atoms = [urllib_unquote(x) for x in quoted_slash.split(path)]
|
|
|
path = "%2F".join(atoms)
|
|
|
environ["PATH_INFO"] = path
|
|
|
|
|
|
@@ -433,7 +446,7 @@ class HTTPRequest(object):
|
|
|
# then all the http headers
|
|
|
try:
|
|
|
self.read_headers()
|
|
|
- except ValueError, ex:
|
|
|
+ except ValueError as ex:
|
|
|
self.simple_response("400 Bad Request", repr(ex.args))
|
|
|
return
|
|
|
|
|
|
@@ -532,7 +545,7 @@ class HTTPRequest(object):
|
|
|
def decode_chunked(self):
|
|
|
"""Decode the 'chunked' transfer coding."""
|
|
|
cl = 0
|
|
|
- data = StringIO.StringIO()
|
|
|
+ data = string_io()
|
|
|
while True:
|
|
|
line = self.rfile.readline().strip().split(";", 1)
|
|
|
chunk_size = int(line.pop(0), 16)
|
|
|
@@ -624,7 +637,7 @@ class HTTPRequest(object):
|
|
|
|
|
|
try:
|
|
|
self.wfile.sendall("".join(buf))
|
|
|
- except socket.error, x:
|
|
|
+ except socket.error as x:
|
|
|
if x.args[0] not in socket_errors_to_ignore:
|
|
|
raise
|
|
|
|
|
|
@@ -760,7 +773,7 @@ if not _fileobject_uses_str_type:
|
|
|
try:
|
|
|
bytes_sent = self.send(data)
|
|
|
data = data[bytes_sent:]
|
|
|
- except socket.error, e:
|
|
|
+ except socket.error as e:
|
|
|
if e.args[0] not in socket_errors_nonblocking:
|
|
|
raise
|
|
|
|
|
|
@@ -777,7 +790,7 @@ if not _fileobject_uses_str_type:
|
|
|
while True:
|
|
|
try:
|
|
|
return self._sock.recv(size)
|
|
|
- except socket.error, e:
|
|
|
+ except socket.error as e:
|
|
|
if (e.args[0] not in socket_errors_nonblocking
|
|
|
and e.args[0] not in socket_error_eintr):
|
|
|
raise
|
|
|
@@ -794,7 +807,7 @@ if not _fileobject_uses_str_type:
|
|
|
buf.seek(0, 2) # seek end
|
|
|
if size < 0:
|
|
|
# Read until EOF
|
|
|
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
|
|
|
+ self._rbuf = string_io() # reset _rbuf. we consume it via buf.
|
|
|
while True:
|
|
|
data = self.recv(rbufsize)
|
|
|
if not data:
|
|
|
@@ -808,11 +821,11 @@ if not _fileobject_uses_str_type:
|
|
|
# Already have size bytes in our buffer? Extract and return.
|
|
|
buf.seek(0)
|
|
|
rv = buf.read(size)
|
|
|
- self._rbuf = StringIO.StringIO()
|
|
|
+ self._rbuf = string_io()
|
|
|
self._rbuf.write(buf.read())
|
|
|
return rv
|
|
|
|
|
|
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
|
|
|
+ self._rbuf = string_io() # reset _rbuf. we consume it via buf.
|
|
|
while True:
|
|
|
left = size - buf_len
|
|
|
# recv() will malloc the amount of memory given as its
|
|
|
@@ -858,7 +871,7 @@ if not _fileobject_uses_str_type:
|
|
|
buf.seek(0)
|
|
|
bline = buf.readline(size)
|
|
|
if bline.endswith('\n') or len(bline) == size:
|
|
|
- self._rbuf = StringIO.StringIO()
|
|
|
+ self._rbuf = string_io()
|
|
|
self._rbuf.write(buf.read())
|
|
|
return bline
|
|
|
del bline
|
|
|
@@ -868,7 +881,7 @@ if not _fileobject_uses_str_type:
|
|
|
# Speed up unbuffered case
|
|
|
buf.seek(0)
|
|
|
buffers = [buf.read()]
|
|
|
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
|
|
|
+ self._rbuf = string_io() # reset _rbuf. we consume it via buf.
|
|
|
data = None
|
|
|
recv = self.recv
|
|
|
while data != "\n":
|
|
|
@@ -879,7 +892,7 @@ if not _fileobject_uses_str_type:
|
|
|
return "".join(buffers)
|
|
|
|
|
|
buf.seek(0, 2) # seek end
|
|
|
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
|
|
|
+ self._rbuf = string_io() # reset _rbuf. we consume it via buf.
|
|
|
while True:
|
|
|
data = self.recv(self._rbufsize)
|
|
|
if not data:
|
|
|
@@ -900,10 +913,10 @@ if not _fileobject_uses_str_type:
|
|
|
if buf_len >= size:
|
|
|
buf.seek(0)
|
|
|
rv = buf.read(size)
|
|
|
- self._rbuf = StringIO.StringIO()
|
|
|
+ self._rbuf = string_io()
|
|
|
self._rbuf.write(buf.read())
|
|
|
return rv
|
|
|
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
|
|
|
+ self._rbuf = string_io() # reset _rbuf. we consume it via buf.
|
|
|
while True:
|
|
|
data = self.recv(self._rbufsize)
|
|
|
if not data:
|
|
|
@@ -946,7 +959,7 @@ else:
|
|
|
try:
|
|
|
bytes_sent = self.send(data)
|
|
|
data = data[bytes_sent:]
|
|
|
- except socket.error, e:
|
|
|
+ except socket.error as e:
|
|
|
if e.args[0] not in socket_errors_nonblocking:
|
|
|
raise
|
|
|
|
|
|
@@ -963,7 +976,7 @@ else:
|
|
|
while True:
|
|
|
try:
|
|
|
return self._sock.recv(size)
|
|
|
- except socket.error, e:
|
|
|
+ except socket.error as e:
|
|
|
if (e.args[0] not in socket_errors_nonblocking
|
|
|
and e.args[0] not in socket_error_eintr):
|
|
|
raise
|
|
|
@@ -1105,7 +1118,7 @@ class SSL_fileobject(CP_fileobject):
|
|
|
time.sleep(self.ssl_retry)
|
|
|
except SSL.WantWriteError:
|
|
|
time.sleep(self.ssl_retry)
|
|
|
- except SSL.SysCallError, e:
|
|
|
+ except SSL.SysCallError as e:
|
|
|
if is_reader and e.args == (-1, 'Unexpected EOF'):
|
|
|
return ""
|
|
|
|
|
|
@@ -1113,7 +1126,7 @@ class SSL_fileobject(CP_fileobject):
|
|
|
if is_reader and errnum in socket_errors_to_ignore:
|
|
|
return ""
|
|
|
raise socket.error(errnum)
|
|
|
- except SSL.Error, e:
|
|
|
+ except SSL.Error as e:
|
|
|
if is_reader and e.args == (-1, 'Unexpected EOF'):
|
|
|
return ""
|
|
|
|
|
|
@@ -1216,7 +1229,7 @@ class HTTPConnection(object):
|
|
|
if req.close_connection:
|
|
|
return
|
|
|
|
|
|
- except socket.error, e:
|
|
|
+ except socket.error as e:
|
|
|
errnum = e.args[0]
|
|
|
if errnum == 'timed out':
|
|
|
# Don't send a 408 if there is no outstanding request; only
|
|
|
@@ -1231,7 +1244,7 @@ class HTTPConnection(object):
|
|
|
return
|
|
|
except (KeyboardInterrupt, SystemExit):
|
|
|
raise
|
|
|
- except FatalSSLAlert, e:
|
|
|
+ except FatalSSLAlert as e:
|
|
|
# Close the connection.
|
|
|
return
|
|
|
except NoSSLError:
|
|
|
@@ -1242,7 +1255,7 @@ class HTTPConnection(object):
|
|
|
"The client sent a plain HTTP request, but "
|
|
|
"this server only speaks HTTPS on this port.")
|
|
|
self.linger = True
|
|
|
- except Exception, e:
|
|
|
+ except Exception as e:
|
|
|
if req and not req.sent_headers:
|
|
|
req.simple_response("500 Internal Server Error", format_exc())
|
|
|
|
|
|
@@ -1317,9 +1330,9 @@ class WorkerThread(threading.Thread):
|
|
|
finally:
|
|
|
conn.close()
|
|
|
self.conn = None
|
|
|
- except Exception, ex:
|
|
|
+ except Exception as ex:
|
|
|
LOG.exception('WSGI (%s) error: %s' % (self, ex))
|
|
|
- except (KeyboardInterrupt, SystemExit), exc:
|
|
|
+ except (KeyboardInterrupt, SystemExit) as exc:
|
|
|
self.server.interrupt = exc
|
|
|
return
|
|
|
|
|
|
@@ -1336,12 +1349,12 @@ class ThreadPool(object):
|
|
|
self.min = min
|
|
|
self.max = max
|
|
|
self._threads = []
|
|
|
- self._queue = Queue.Queue()
|
|
|
+ self._queue = queue.Queue()
|
|
|
self.get = self._queue.get
|
|
|
|
|
|
def start(self):
|
|
|
"""Start the pool of threads."""
|
|
|
- for i in xrange(self.min):
|
|
|
+ for i in range(self.min):
|
|
|
self._threads.append(WorkerThread(self.server))
|
|
|
for worker in self._threads:
|
|
|
worker.setName("CP WSGIServer " + worker.getName())
|
|
|
@@ -1362,7 +1375,7 @@ class ThreadPool(object):
|
|
|
|
|
|
def grow(self, amount):
|
|
|
"""Spawn new worker threads (not above self.max)."""
|
|
|
- for i in xrange(amount):
|
|
|
+ for i in range(amount):
|
|
|
if self.max > 0 and len(self._threads) >= self.max:
|
|
|
break
|
|
|
worker = WorkerThread(self.server)
|
|
|
@@ -1380,7 +1393,7 @@ class ThreadPool(object):
|
|
|
amount -= 1
|
|
|
|
|
|
if amount > 0:
|
|
|
- for i in xrange(min(amount, len(self._threads) - self.min)):
|
|
|
+ for i in range(min(amount, len(self._threads) - self.min)):
|
|
|
# Put a number of shutdown requests on the queue equal
|
|
|
# to 'amount'. Once each of those is processed by a worker,
|
|
|
# that worker will terminate and be culled from our list
|
|
|
@@ -1417,12 +1430,12 @@ class ThreadPool(object):
|
|
|
except (AssertionError,
|
|
|
# Ignore repeated Ctrl-C.
|
|
|
# See http://www.cherrypy.org/ticket/691.
|
|
|
- KeyboardInterrupt), exc1:
|
|
|
+ KeyboardInterrupt) as exc1:
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
-class SSLConnection:
|
|
|
+class SSLConnection(object):
|
|
|
"""A thread-safe wrapper for an SSL.Connection.
|
|
|
|
|
|
*args: the arguments to create the wrapped SSL.Connection(*args).
|
|
|
@@ -1440,13 +1453,13 @@ class SSLConnection:
|
|
|
'sock_shutdown', 'get_peer_certificate', 'want_read',
|
|
|
'want_write', 'set_connect_state', 'set_accept_state',
|
|
|
'connect_ex', 'sendall', 'settimeout'):
|
|
|
- exec """def %s(self, *args):
|
|
|
+ exec("""def %s(self, *args):
|
|
|
self._lock.acquire()
|
|
|
try:
|
|
|
return self._ssl_conn.%s(*args)
|
|
|
finally:
|
|
|
self._lock.release()
|
|
|
-""" % (f, f)
|
|
|
+""" % (f, f))
|
|
|
|
|
|
|
|
|
try:
|
|
|
@@ -1626,7 +1639,7 @@ class CherryPyWSGIServer(object):
|
|
|
|
|
|
# So everyone can access the socket...
|
|
|
try:
|
|
|
- os.chmod(self.bind_addr, 0777)
|
|
|
+ os.chmod(self.bind_addr, 0o777)
|
|
|
except IOError:
|
|
|
pass
|
|
|
|
|
|
@@ -1648,14 +1661,14 @@ class CherryPyWSGIServer(object):
|
|
|
af, socktype, proto, canonname, sa = res
|
|
|
try:
|
|
|
self._bind(af, socktype, proto)
|
|
|
- except socket.error, msg:
|
|
|
+ except socket.error as msg:
|
|
|
if self.socket:
|
|
|
self.socket.close()
|
|
|
self.socket = None
|
|
|
continue
|
|
|
break
|
|
|
if not self.socket:
|
|
|
- raise socket.error, msg
|
|
|
+ raise socket.error(msg)
|
|
|
|
|
|
def listen_and_loop(self):
|
|
|
"""
|
|
|
@@ -1702,7 +1715,7 @@ class CherryPyWSGIServer(object):
|
|
|
ctx.use_certificate_file(self.ssl_certificate)
|
|
|
if self.ssl_certificate_chain:
|
|
|
ctx.use_certificate_chain_file(self.ssl_certificate_chain)
|
|
|
- except Exception, ex:
|
|
|
+ except Exception as ex:
|
|
|
logging.exception('SSL key and certificate could not be found or have a problem')
|
|
|
raise ex
|
|
|
ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
|
|
|
@@ -1761,7 +1774,7 @@ class CherryPyWSGIServer(object):
|
|
|
# notice keyboard interrupts on Win32, which don't interrupt
|
|
|
# accept() by default
|
|
|
return
|
|
|
- except socket.error, x:
|
|
|
+ except socket.error as x:
|
|
|
if x.args[0] in socket_error_eintr:
|
|
|
# I *think* this is right. EINTR should occur when a signal
|
|
|
# is received during the accept() call; all docs say retry
|
|
|
@@ -1798,7 +1811,7 @@ class CherryPyWSGIServer(object):
|
|
|
# Touch our own socket to make accept() return immediately.
|
|
|
try:
|
|
|
host, port = sock.getsockname()[:2]
|
|
|
- except socket.error, x:
|
|
|
+ except socket.error as x:
|
|
|
if x.args[0] not in socket_errors_to_ignore:
|
|
|
raise
|
|
|
else:
|