diff options
Diffstat (limited to 'eventlet')
| -rw-r--r-- | eventlet/green/http/__init__.py | 2 | ||||
| -rw-r--r-- | eventlet/green/http/client.py | 9 | ||||
| -rw-r--r-- | eventlet/green/http/cookiejar.py | 13 | ||||
| -rw-r--r-- | eventlet/green/http/cookies.py | 7 | ||||
| -rw-r--r-- | eventlet/green/http/server.py | 17 | ||||
| -rw-r--r-- | eventlet/green/selectors.py | 11 | ||||
| -rw-r--r-- | eventlet/green/subprocess.py | 12 | ||||
| -rw-r--r-- | eventlet/green/urllib.py | 38 | ||||
| -rw-r--r-- | eventlet/green/urllib/__init__.py | 40 | ||||
| -rw-r--r-- | eventlet/green/urllib/error.py | 4 | ||||
| -rw-r--r-- | eventlet/green/urllib/parse.py | 3 | ||||
| -rw-r--r-- | eventlet/green/urllib/request.py | 44 | ||||
| -rw-r--r-- | eventlet/green/urllib/response.py | 3 | ||||
| -rw-r--r-- | eventlet/greenio/__init__.py | 8 | ||||
| -rw-r--r-- | eventlet/greenio/base.py (renamed from eventlet/greenio.py) | 238 | ||||
| -rw-r--r-- | eventlet/greenio/py2.py | 222 | ||||
| -rw-r--r-- | eventlet/greenio/py3.py | 191 | ||||
| -rw-r--r-- | eventlet/wsgi.py | 30 |
18 files changed, 603 insertions, 289 deletions
diff --git a/eventlet/green/http/__init__.py b/eventlet/green/http/__init__.py new file mode 100644 index 0000000..c9e2a23 --- /dev/null +++ b/eventlet/green/http/__init__.py @@ -0,0 +1,2 @@ +from eventlet.support import six +assert six.PY3, 'This is a Python 3 module' diff --git a/eventlet/green/http/client.py b/eventlet/green/http/client.py new file mode 100644 index 0000000..480a252 --- /dev/null +++ b/eventlet/green/http/client.py @@ -0,0 +1,9 @@ +from eventlet import patcher +from eventlet.green import os, socket +from eventlet.green.urllib import parse as urllib_parse + +patcher.inject('http.client', globals(), + ('os', os), ('socket', socket), ('urllib.parse', urllib_parse)) + +del patcher +del urllib_parse diff --git a/eventlet/green/http/cookiejar.py b/eventlet/green/http/cookiejar.py new file mode 100644 index 0000000..5e511d2 --- /dev/null +++ b/eventlet/green/http/cookiejar.py @@ -0,0 +1,13 @@ +from eventlet.green import threading, time +from eventlet.green.http import client +from eventlet.green.urllib import parse as urllib_parse, request as urllib_request +from eventlet import patcher + +patcher.inject('http.cookiejar', globals(), + ('http.client', client), ('threading', threading), + ('urllib.parse', urllib_parse), ('urllib.request', urllib_request), + ('time', time)) + +del urllib_request +del urllib_parse +del patcher diff --git a/eventlet/green/http/cookies.py b/eventlet/green/http/cookies.py new file mode 100644 index 0000000..e139069 --- /dev/null +++ b/eventlet/green/http/cookies.py @@ -0,0 +1,7 @@ +from eventlet import patcher +from eventlet.green import time + +patcher.inject('http.cookies', globals()) +_getdate = patcher.patch_function(_getdate, ('time', time)) + +del patcher diff --git a/eventlet/green/http/server.py b/eventlet/green/http/server.py new file mode 100644 index 0000000..35c3ab2 --- /dev/null +++ b/eventlet/green/http/server.py @@ -0,0 +1,17 @@ +from eventlet import patcher +from eventlet.green import os, time, select, socket, SocketServer, subprocess +from eventlet.green.http import client +from eventlet.green.urllib import parse as urllib_parse + +patcher.inject('http.server', globals(), + ('http.client', client), ('os', os), ('select', select), + ('socket', socket), ('socketserver', SocketServer), ('time', time), + ('urllib.parse', urllib_parse)) + + +CGIHTTPRequestHandler.run_cgi = patcher.patch_function( + CGIHTTPRequestHandler.run_cgi, ('subprocess', subprocess)) + +del urllib_parse +del client +del patcher diff --git a/eventlet/green/selectors.py b/eventlet/green/selectors.py new file mode 100644 index 0000000..26427ec --- /dev/null +++ b/eventlet/green/selectors.py @@ -0,0 +1,11 @@ +import sys + +from eventlet import patcher +from eventlet.green import select + +patcher.inject('selectors', globals(), ('select', select)) + +del patcher + +if sys.platform != 'win32': + SelectSelector._select = staticmethod(select.select) diff --git a/eventlet/green/subprocess.py b/eventlet/green/subprocess.py index 1d7c49a..7ce38cf 100644 --- a/eventlet/green/subprocess.py +++ b/eventlet/green/subprocess.py @@ -1,15 +1,21 @@ import errno -import time +import sys from types import FunctionType import eventlet from eventlet import greenio from eventlet import patcher -from eventlet.green import select +from eventlet.green import select, threading, time from eventlet.support import six -patcher.inject('subprocess', globals(), ('select', select)) +to_patch = [('select', select), ('threading', threading), ('time', time)] + +if sys.version_info > (3, 4): + from eventlet.green import selectors + to_patch.append(('selectors', selectors)) + +patcher.inject('subprocess', globals(), *to_patch) subprocess_orig = __import__("subprocess") diff --git a/eventlet/green/urllib.py b/eventlet/green/urllib.py deleted file mode 100644 index f5c8f13..0000000 --- a/eventlet/green/urllib.py +++ /dev/null @@ -1,38 +0,0 @@ -from eventlet import patcher -from eventlet.green import socket -from eventlet.green import time -from eventlet.green import httplib -from eventlet.green import ftplib - -to_patch = [('socket', socket), ('httplib', httplib), - ('time', time), ('ftplib', ftplib)] -try: - from eventlet.green import ssl - to_patch.append(('ssl', ssl)) -except ImportError: - pass - -patcher.inject('urllib', globals(), *to_patch) -try: - URLopener -except NameError: - patcher.inject('urllib.request', globals(), *to_patch) - - -# patch a bunch of things that have imports inside the -# function body; this is lame and hacky but I don't feel -# too bad because urllib is a hacky pile of junk that no -# one should be using anyhow -URLopener.open_http = patcher.patch_function(URLopener.open_http, ('httplib', httplib)) -if hasattr(URLopener, 'open_https'): - URLopener.open_https = patcher.patch_function(URLopener.open_https, ('httplib', httplib)) - -URLopener.open_ftp = patcher.patch_function(URLopener.open_ftp, ('ftplib', ftplib)) -ftpwrapper.init = patcher.patch_function(ftpwrapper.init, ('ftplib', ftplib)) -ftpwrapper.retrfile = patcher.patch_function(ftpwrapper.retrfile, ('ftplib', ftplib)) - -del patcher - -# Run test program when run as a script -if __name__ == '__main__': - main() diff --git a/eventlet/green/urllib/__init__.py b/eventlet/green/urllib/__init__.py new file mode 100644 index 0000000..7cb4ea6 --- /dev/null +++ b/eventlet/green/urllib/__init__.py @@ -0,0 +1,40 @@ +from eventlet import patcher +from eventlet.green import socket +from eventlet.green import time +from eventlet.green import httplib +from eventlet.green import ftplib +from eventlet.support import six + +if six.PY2: + to_patch = [('socket', socket), ('httplib', httplib), + ('time', time), ('ftplib', ftplib)] + try: + from eventlet.green import ssl + to_patch.append(('ssl', ssl)) + except ImportError: + pass + + patcher.inject('urllib', globals(), *to_patch) + try: + URLopener + except NameError: + patcher.inject('urllib.request', globals(), *to_patch) + + + # patch a bunch of things that have imports inside the + # function body; this is lame and hacky but I don't feel + # too bad because urllib is a hacky pile of junk that no + # one should be using anyhow + URLopener.open_http = patcher.patch_function(URLopener.open_http, ('httplib', httplib)) + if hasattr(URLopener, 'open_https'): + URLopener.open_https = patcher.patch_function(URLopener.open_https, ('httplib', httplib)) + + URLopener.open_ftp = patcher.patch_function(URLopener.open_ftp, ('ftplib', ftplib)) + ftpwrapper.init = patcher.patch_function(ftpwrapper.init, ('ftplib', ftplib)) + ftpwrapper.retrfile = patcher.patch_function(ftpwrapper.retrfile, ('ftplib', ftplib)) + + del patcher + + # Run test program when run as a script + if __name__ == '__main__': + main() diff --git a/eventlet/green/urllib/error.py b/eventlet/green/urllib/error.py new file mode 100644 index 0000000..6913813 --- /dev/null +++ b/eventlet/green/urllib/error.py @@ -0,0 +1,4 @@ +from eventlet import patcher +from eventlet.green.urllib import response +patcher.inject('urllib.error', globals(), ('urllib.response', response)) +del patcher diff --git a/eventlet/green/urllib/parse.py b/eventlet/green/urllib/parse.py new file mode 100644 index 0000000..f3a8924 --- /dev/null +++ b/eventlet/green/urllib/parse.py @@ -0,0 +1,3 @@ +from eventlet import patcher +patcher.inject('urllib.parse', globals()) +del patcher diff --git a/eventlet/green/urllib/request.py b/eventlet/green/urllib/request.py new file mode 100644 index 0000000..8160bb9 --- /dev/null +++ b/eventlet/green/urllib/request.py @@ -0,0 +1,44 @@ +from eventlet import patcher +from eventlet.green import ftplib, os, socket, time +from eventlet.green.http import client as http_client +from eventlet.green.urllib import error, parse, response + +# TODO should we also have green email version? +# import email + + +to_patch = [ + ('http.client', http_client), + ('os', os), + ('socket', socket), + ('time', time), + ('urllib.error', error), + ('urllib.parse', parse), + ('urllib.response', response), +] + +try: + from eventlet.green import ssl +except ImportError: + pass +else: + to_patch.append(('ssl', ssl)) + +patcher.inject('urllib.request', globals(), *to_patch) +del to_patch + +to_patch_in_functions = [('ftplib', ftplib)] +del ftplib + +FTPHandler.ftp_open = patcher.patch_function(FTPHandler.ftp_open, *to_patch_in_functions) +URLopener.open_ftp = patcher.patch_function(URLopener.open_ftp, *to_patch_in_functions) + +ftperrors = patcher.patch_function(ftperrors, *to_patch_in_functions) + +ftpwrapper.init = patcher.patch_function(ftpwrapper.init, *to_patch_in_functions) +ftpwrapper.retrfile = patcher.patch_function(ftpwrapper.retrfile, *to_patch_in_functions) + +del error +del parse +del response +del to_patch_in_functions diff --git a/eventlet/green/urllib/response.py b/eventlet/green/urllib/response.py new file mode 100644 index 0000000..f9aaba5 --- /dev/null +++ b/eventlet/green/urllib/response.py @@ -0,0 +1,3 @@ +from eventlet import patcher +patcher.inject('urllib.response', globals()) +del patcher diff --git a/eventlet/greenio/__init__.py b/eventlet/greenio/__init__.py new file mode 100644 index 0000000..72cd33e --- /dev/null +++ b/eventlet/greenio/__init__.py @@ -0,0 +1,8 @@ +from eventlet.support import six + +from eventlet.greenio.base import * # noqa + +if six.PY2: + from eventlet.greenio.py2 import * # noqa +else: + from eventlet.greenio.py3 import * # noqa diff --git a/eventlet/greenio.py b/eventlet/greenio/base.py index 46e3909..8da51ca 100644 --- a/eventlet/greenio.py +++ b/eventlet/greenio/base.py @@ -7,9 +7,13 @@ import time import warnings from eventlet.support import get_errno, six -from eventlet.hubs import trampoline, notify_close, notify_opened, IOClosed +from eventlet.hubs import trampoline, notify_opened, IOClosed -__all__ = ['GreenSocket', 'GreenPipe', 'shutdown_safe'] +__all__ = [ + 'GreenSocket', '_GLOBAL_DEFAULT_TIMEOUT', 'set_nonblocking', + 'SOCKET_CLOSED', 'CONNECT_ERR', 'CONNECT_SUCCESS', + 'shutdown_safe', 'SSL', +] BUFFER_SIZE = 4096 CONNECT_ERR = set((errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK)) @@ -17,11 +21,8 @@ CONNECT_SUCCESS = set((0, errno.EISCONN)) if sys.platform[:3] == "win": CONNECT_ERR.add(errno.WSAEINVAL) # Bug 67 -if six.PY3: - from io import IOBase as file - _fileobject = socket.SocketIO -elif six.PY2: - _fileobject = socket._fileobject +if six.PY2: + _python2_fileobject = socket._fileobject def socket_connect(descriptor, address): @@ -293,7 +294,7 @@ class GreenSocket(object): else: def makefile(self, *args, **kwargs): dupped = self.dup() - res = _fileobject(dupped, *args, **kwargs) + res = _python2_fileobject(dupped, *args, **kwargs) if hasattr(dupped, "_drop"): dupped._drop() return res @@ -418,119 +419,11 @@ class GreenSocket(object): getattr(self.fd, '_sock', self.fd)._drop() -class _SocketDuckForFd(object): - """Class implementing all socket method used by _fileobject - in cooperative manner using low level os I/O calls. - """ - _refcount = 0 - - def __init__(self, fileno): - self._fileno = fileno - notify_opened(fileno) - self._closed = False - - def _trampoline(self, fd, read=False, write=False, timeout=None, timeout_exc=None): - if self._closed: - # Don't trampoline if we're already closed. - raise IOClosed() - try: - return trampoline(fd, read=read, write=write, timeout=timeout, - timeout_exc=timeout_exc, - mark_as_closed=self._mark_as_closed) - except IOClosed: - # Our fileno has been obsoleted. Defang ourselves to - # prevent spurious closes. - self._mark_as_closed() - raise - - def _mark_as_closed(self): - self._closed = True - - @property - def _sock(self): - return self - - def fileno(self): - return self._fileno - - def recv(self, buflen): - while True: - try: - data = os.read(self._fileno, buflen) - return data - except OSError as e: - if get_errno(e) not in SOCKET_BLOCKING: - raise IOError(*e.args) - self._trampoline(self, read=True) - - def recv_into(self, buf, nbytes=0, flags=0): - if nbytes == 0: - nbytes = len(buf) - data = self.recv(nbytes) - buf[:nbytes] = data - return len(data) - - def send(self, data): - while True: - try: - return os.write(self._fileno, data) - except OSError as e: - if get_errno(e) not in SOCKET_BLOCKING: - raise IOError(*e.args) - else: - trampoline(self, write=True) - - def sendall(self, data): - len_data = len(data) - os_write = os.write - fileno = self._fileno - try: - total_sent = os_write(fileno, data) - except OSError as e: - if get_errno(e) != errno.EAGAIN: - raise IOError(*e.args) - total_sent = 0 - while total_sent < len_data: - self._trampoline(self, write=True) - try: - total_sent += os_write(fileno, data[total_sent:]) - except OSError as e: - if get_errno(e) != errno. EAGAIN: - raise IOError(*e.args) - - def __del__(self): - self._close() - - def _close(self): - notify_close(self._fileno) - self._mark_as_closed() - try: - os.close(self._fileno) - except: - # os.close may fail if __init__ didn't complete - # (i.e file dscriptor passed to popen was invalid - pass - - def __repr__(self): - return "%s:%d" % (self.__class__.__name__, self._fileno) - - def _reuse(self): - self._refcount += 1 - - def _drop(self): - self._refcount -= 1 - if self._refcount == 0: - self._close() - # Python3 - _decref_socketios = _drop - - -def _operationOnClosedFile(*args, **kwargs): +def _operation_on_closed_file(*args, **kwargs): raise ValueError("I/O operation on closed file") -class GreenPipe(_fileobject): - """ +greenpipe_doc = """ GreenPipe is a cooperative replacement for file class. It will cooperate on pipes. It will block on regular file. Differneces from file class: @@ -542,115 +435,6 @@ class GreenPipe(_fileobject): - file argument can be descriptor, file name or file object. """ - def __init__(self, f, mode='r', bufsize=-1): - if not isinstance(f, six.string_types + (int, file)): - raise TypeError('f(ile) should be int, str, unicode or file, not %r' % f) - - if isinstance(f, six.string_types): - f = open(f, mode, 0) - - if isinstance(f, int): - fileno = f - self._name = "<fd:%d>" % fileno - else: - fileno = os.dup(f.fileno()) - self._name = f.name - if not ( - f.mode == mode or - six.PY3 and f.mode == 'rb+' and mode == 'wb+' - ): - raise ValueError('file.mode %r does not match mode parameter %r' % (f.mode, mode)) - self._name = f.name - f.close() - - if six.PY3: - mode = mode.rstrip('+') - - super(GreenPipe, self).__init__(_SocketDuckForFd(fileno), mode) - set_nonblocking(self) - self.softspace = 0 - - if six.PY3: - def write(self, data): - while data: - sent = _fileobject.write(self, data) - data = data[sent:] - - @property - def name(self): - return self._name - - def __repr__(self): - return "<%s %s %r, mode %r at 0x%x>" % ( - self.closed and 'closed' or 'open', - self.__class__.__name__, - self.name, - self.mode, - (id(self) < 0) and (sys.maxint + id(self)) or id(self)) - - def close(self): - super(GreenPipe, self).close() - for method in [ - 'fileno', 'flush', 'isatty', 'next', 'read', 'readinto', - 'readline', 'readlines', 'seek', 'tell', 'truncate', - 'write', 'xreadlines', '__iter__', '__next__', 'writelines']: - setattr(self, method, _operationOnClosedFile) - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - def _get_readahead_len(self): - return len(self._rbuf.getvalue()) - - def _clear_readahead_buf(self): - len = self._get_readahead_len() - if len > 0: - self.read(len) - - def tell(self): - self.flush() - try: - return os.lseek(self.fileno(), 0, 1) - self._get_readahead_len() - except OSError as e: - raise IOError(*e.args) - - def seek(self, offset, whence=0): - self.flush() - if whence == 1 and offset == 0: # tell synonym - return self.tell() - if whence == 1: # adjust offset by what is read ahead - offset -= self._get_readahead_len() - try: - rv = os.lseek(self.fileno(), offset, whence) - except OSError as e: - raise IOError(*e.args) - else: - self._clear_readahead_buf() - return rv - - if getattr(file, "truncate", None): # not all OSes implement truncate - def truncate(self, size=-1): - self.flush() - if size == -1: - size = self.tell() - try: - rv = os.ftruncate(self.fileno(), size) - except OSError as e: - raise IOError(*e.args) - else: - self.seek(size) # move position&clear buffer - return rv - - def isatty(self): - try: - return os.isatty(self.fileno()) - except OSError as e: - raise IOError(*e.args) - - # import SSL module here so we can refer to greenio.SSL.exceptionclass try: from OpenSSL import SSL diff --git a/eventlet/greenio/py2.py b/eventlet/greenio/py2.py new file mode 100644 index 0000000..6fa6cb7 --- /dev/null +++ b/eventlet/greenio/py2.py @@ -0,0 +1,222 @@ +import errno +import os + +from eventlet.greenio.base import ( + _operation_on_closed_file, + greenpipe_doc, + set_nonblocking, + socket, + SOCKET_BLOCKING, +) +from eventlet.hubs import trampoline, notify_close, notify_opened, IOClosed +from eventlet.support import get_errno, six + +__all__ = ['_fileobject', 'GreenPipe'] + +_fileobject = socket._fileobject + + +class GreenPipe(_fileobject): + + __doc__ = greenpipe_doc + + def __init__(self, f, mode='r', bufsize=-1): + if not isinstance(f, six.string_types + (int, file)): + raise TypeError('f(ile) should be int, str, unicode or file, not %r' % f) + + if isinstance(f, six.string_types): + f = open(f, mode, 0) + + if isinstance(f, int): + fileno = f + self._name = "<fd:%d>" % fileno + else: + fileno = os.dup(f.fileno()) + self._name = f.name + if f.mode != mode: + raise ValueError('file.mode %r does not match mode parameter %r' % (f.mode, mode)) + self._name = f.name + f.close() + + super(GreenPipe, self).__init__(_SocketDuckForFd(fileno), mode) + set_nonblocking(self) + self.softspace = 0 + + @property + def name(self): + return self._name + + def __repr__(self): + return "<%s %s %r, mode %r at 0x%x>" % ( + self.closed and 'closed' or 'open', + self.__class__.__name__, + self.name, + self.mode, + (id(self) < 0) and (sys.maxint + id(self)) or id(self)) + + def close(self): + super(GreenPipe, self).close() + for method in [ + 'fileno', 'flush', 'isatty', 'next', 'read', 'readinto', + 'readline', 'readlines', 'seek', 'tell', 'truncate', + 'write', 'xreadlines', '__iter__', '__next__', 'writelines']: + setattr(self, method, _operation_on_closed_file) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def _get_readahead_len(self): + return len(self._rbuf.getvalue()) + + def _clear_readahead_buf(self): + len = self._get_readahead_len() + if len > 0: + self.read(len) + + def tell(self): + self.flush() + try: + return os.lseek(self.fileno(), 0, 1) - self._get_readahead_len() + except OSError as e: + raise IOError(*e.args) + + def seek(self, offset, whence=0): + self.flush() + if whence == 1 and offset == 0: # tell synonym + return self.tell() + if whence == 1: # adjust offset by what is read ahead + offset -= self._get_readahead_len() + try: + rv = os.lseek(self.fileno(), offset, whence) + except OSError as e: + raise IOError(*e.args) + else: + self._clear_readahead_buf() + return rv + + if getattr(file, "truncate", None): # not all OSes implement truncate + def truncate(self, size=-1): + self.flush() + if size == -1: + size = self.tell() + try: + rv = os.ftruncate(self.fileno(), size) + except OSError as e: + raise IOError(*e.args) + else: + self.seek(size) # move position&clear buffer + return rv + + def isatty(self): + try: + return os.isatty(self.fileno()) + except OSError as e: + raise IOError(*e.args) + + +class _SocketDuckForFd(object): + """Class implementing all socket method used by _fileobject + in cooperative manner using low level os I/O calls. + """ + _refcount = 0 + + def __init__(self, fileno): + self._fileno = fileno + notify_opened(fileno) + self._closed = False + + def _trampoline(self, fd, read=False, write=False, timeout=None, timeout_exc=None): + if self._closed: + # Don't trampoline if we're already closed. + raise IOClosed() + try: + return trampoline(fd, read=read, write=write, timeout=timeout, + timeout_exc=timeout_exc, + mark_as_closed=self._mark_as_closed) + except IOClosed: + # Our fileno has been obsoleted. Defang ourselves to + # prevent spurious closes. + self._mark_as_closed() + raise + + def _mark_as_closed(self): + self._closed = True + + @property + def _sock(self): + return self + + def fileno(self): + return self._fileno + + def recv(self, buflen): + while True: + try: + data = os.read(self._fileno, buflen) + return data + except OSError as e: + if get_errno(e) not in SOCKET_BLOCKING: + raise IOError(*e.args) + self._trampoline(self, read=True) + + def recv_into(self, buf, nbytes=0, flags=0): + if nbytes == 0: + nbytes = len(buf) + data = self.recv(nbytes) + buf[:nbytes] = data + return len(data) + + def send(self, data): + while True: + try: + return os.write(self._fileno, data) + except OSError as e: + if get_errno(e) not in SOCKET_BLOCKING: + raise IOError(*e.args) + else: + trampoline(self, write=True) + + def sendall(self, data): + len_data = len(data) + os_write = os.write + fileno = self._fileno + try: + total_sent = os_write(fileno, data) + except OSError as e: + if get_errno(e) != errno.EAGAIN: + raise IOError(*e.args) + total_sent = 0 + while total_sent < len_data: + self._trampoline(self, write=True) + try: + total_sent += os_write(fileno, data[total_sent:]) + except OSError as e: + if get_errno(e) != errno. EAGAIN: + raise IOError(*e.args) + + def __del__(self): + self._close() + + def _close(self): + notify_close(self._fileno) + self._mark_as_closed() + try: + os.close(self._fileno) + except: + # os.close may fail if __init__ didn't complete + # (i.e file dscriptor passed to popen was invalid + pass + + def __repr__(self): + return "%s:%d" % (self.__class__.__name__, self._fileno) + + def _reuse(self): + self._refcount += 1 + + def _drop(self): + self._refcount -= 1 + if self._refcount == 0: + self._close() diff --git a/eventlet/greenio/py3.py b/eventlet/greenio/py3.py new file mode 100644 index 0000000..e41663a --- /dev/null +++ b/eventlet/greenio/py3.py @@ -0,0 +1,191 @@ +import _pyio as _original_pyio +import errno +import os as _original_os +import socket as _original_socket +from io import ( + BufferedRandom as _OriginalBufferedRandom, + BufferedReader as _OriginalBufferedReader, + BufferedWriter as _OriginalBufferedWriter, + DEFAULT_BUFFER_SIZE, + TextIOWrapper as _OriginalTextIOWrapper, + IOBase as _OriginalIOBase, +) +from types import FunctionType + +from eventlet.greenio.base import ( + _operation_on_closed_file, + greenpipe_doc, + set_nonblocking, + SOCKET_BLOCKING, +) +from eventlet.hubs import notify_close, notify_opened, IOClosed, trampoline +from eventlet.support import get_errno, six + +__all__ = ['_fileobject', 'GreenPipe'] + +# TODO get rid of this, it only seems like the original _fileobject +_fileobject = _original_socket.SocketIO + +# Large part of the following code is copied from the original +# eventlet.greenio module + + +class GreenFileIO(_OriginalIOBase): + def __init__(self, name, mode='r', closefd=True, opener=None): + if isinstance(name, int): + fileno = name + self._name = "<fd:%d>" % fileno + else: + assert isinstance(name, six.string_types) + with open(name, mode) as fd: + self._name = fd.name + fileno = _original_os.dup(fd.fileno()) + + notify_opened(fileno) + self._fileno = fileno + self._mode = mode + self._closed = False + set_nonblocking(self) + self._seekable = None + + @property + def closed(self): + return self._closed + + def seekable(self): + if self._seekable is None: + try: + _original_os.lseek(self._fileno, 0, _original_os.SEEK_CUR) + except IOError as e: + if get_errno(e) == errno.ESPIPE: + self._seekable = False + else: + raise + else: + self._seekable = True + + return self._seekable + + def readable(self): + return 'r' in self._mode or '+' in self._mode + + def writable(self): + return 'w' in self._mode or '+' in self._mode + + def fileno(self): + return self._fileno + + def read(self, buflen): + while True: + try: + return _original_os.read(self._fileno, buflen) + except OSError as e: + if get_errno(e) not in SOCKET_BLOCKING: + raise IOError(*e.args) + self._trampoline(self, read=True) + + def readinto(self, b): + up_to = len(b) + data = self.read(up_to) + bytes_read = len(data) + b[:bytes_read] = data + return bytes_read + + def isatty(self): + try: + return _original_os.isatty(self.fileno()) + except OSError as e: + raise IOError(*e.args) + + def _trampoline(self, fd, read=False, write=False, timeout=None, timeout_exc=None): + if self._closed: + # Don't trampoline if we're already closed. + raise IOClosed() + try: + return trampoline(fd, read=read, write=write, timeout=timeout, + timeout_exc=timeout_exc, + mark_as_closed=self._mark_as_closed) + except IOClosed: + # Our fileno has been obsoleted. Defang ourselves to + # prevent spurious closes. + self._mark_as_closed() + raise + + def _mark_as_closed(self): + """ Mark this socket as being closed """ + self._closed = True + + def write(self, data): + while True: + try: + return _original_os.write(self._fileno, data) + except OSError as e: + if get_errno(e) not in SOCKET_BLOCKING: + raise IOError(*e.args) + else: + trampoline(self, write=True) + + def close(self): + _original_os.close(self._fileno) + notify_close(self._fileno) + self._closed = True + for method in [ + 'fileno', 'flush', 'isatty', 'next', 'read', 'readinto', + 'readline', 'readlines', 'seek', 'tell', 'truncate', + 'write', 'xreadlines', '__iter__', '__next__', 'writelines']: + setattr(self, method, _operation_on_closed_file) + + def truncate(self, size=-1): + if size == -1: + size = self.tell() + try: + rv = _original_os.ftruncate(self._fileno, size) + except OSError as e: + raise IOError(*e.args) + else: + self.seek(size) # move position&clear buffer + return rv + + def seek(self, offset, whence=_original_os.SEEK_SET): + try: + return _original_os.lseek(self._fileno, offset, whence) + except OSError as e: + raise IOError(*e.args) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + +_open_environment = dict(globals()) +_open_environment.update(dict( + BufferedRandom=_OriginalBufferedRandom, + BufferedWriter=_OriginalBufferedWriter, + BufferedReader=_OriginalBufferedReader, + TextIOWrapper=_OriginalTextIOWrapper, + FileIO=GreenFileIO, + os=_original_os, +)) + +_open = FunctionType( + six.get_function_code(_original_pyio.open), + _open_environment, +) + + +def GreenPipe(name, mode="r", buffering=-1, encoding=None, errors=None, + newline=None, closefd=True, opener=None): + try: + fileno = name.fileno() + except AttributeError: + pass + else: + fileno = _original_os.dup(fileno) + name.close() + name = fileno + + return _open(name, mode, buffering, encoding, errors, newline, closefd, opener) + +GreenPipe.__doc__ = greenpipe_doc diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index e69d107..05b629f 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -8,12 +8,13 @@ import warnings from eventlet.green import BaseHTTPServer from eventlet.green import socket -from eventlet.green import urllib from eventlet import greenio from eventlet import greenpool from eventlet import support from eventlet.support import six +from eventlet.support.six.moves import urllib + DEFAULT_MAX_SIMULTANEOUS_REQUESTS = 1024 DEFAULT_MAX_HTTP_VERSION = 'HTTP/1.1' @@ -395,24 +396,8 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): towrite.append(six.b("%x" % (len(data),)) + b"\r\n" + data + b"\r\n") else: towrite.append(data) - try: - _writelines(towrite) - length[0] = length[0] + sum(map(len, towrite)) - except UnicodeEncodeError: - self.server.log_message( - "Encountered non-ascii unicode while attempting to write" - "wsgi response: %r" % - [x for x in towrite if isinstance(x, six.text_type)]) - self.server.log_message(traceback.format_exc()) - _writelines( - ["HTTP/1.1 500 Internal Server Error\r\n", - "Connection: close\r\n", - "Content-type: text/plain\r\n", - "Content-length: 98\r\n", - "Date: %s\r\n" % format_date_time(time.time()), - "\r\n", - ("Internal Server Error: wsgi application passed " - "a unicode object to the server instead of a string.")]) + _writelines(towrite) + length[0] = length[0] + sum(map(len, towrite)) def start_response(status, response_headers, exc_info=None): status_code[0] = status.split()[0] @@ -456,6 +441,9 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): minimum_write_chunk_size = int(self.environ.get( 'eventlet.minimum_write_chunk_size', self.minimum_chunk_size)) for data in result: + if isinstance(data, six.text_type): + data = data.encode('ascii') + towrite.append(data) towrite_size += len(data) if towrite_size >= minimum_write_chunk_size: @@ -472,7 +460,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): self.close_connection = 1 tb = traceback.format_exc() self.server.log_message(tb) - if not headers_set: + if not headers_sent: err_body = six.b(tb) if self.server.debug else b'' start_response("500 Internal Server Error", [('Content-type', 'text/plain'), @@ -522,7 +510,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): pq = self.path.split('?', 1) env['RAW_PATH_INFO'] = pq[0] - env['PATH_INFO'] = urllib.unquote(pq[0]) + env['PATH_INFO'] = urllib.parse.unquote(pq[0]) if len(pq) > 1: env['QUERY_STRING'] = pq[1] |
