From ef1afb440736d8e179245f2f365ad697199dbd25 Mon Sep 17 00:00:00 2001 From: "which.linden" Date: Mon, 8 Oct 2007 03:12:35 -0400 Subject: [PATCH] [svn r16] svn merge -r8:14 https://svn.secondlife.com/svn/eventlet/branches/beta-1 into https://svn.secondlife.com/svn/eventlet/trunk --- eventlet/api.py | 9 +++-- eventlet/httpc.py | 80 +++++++++++++++++++++++++++++++------------ eventlet/httpd.py | 6 ++-- eventlet/processes.py | 3 ++ eventlet/selecthub.py | 4 +-- 5 files changed, 73 insertions(+), 29 deletions(-) diff --git a/eventlet/api.py b/eventlet/api.py index b2e3c53..18bb4fb 100644 --- a/eventlet/api.py +++ b/eventlet/api.py @@ -38,9 +38,12 @@ except ImportError: pylibsupport.emulate() greenlet = sys.modules['greenlet'] except ImportError: - import stacklesssupport - stacklesssupport.emulate() - greenlet = sys.modules['greenlet'] + try: + import stacklesssupport + stacklesssupport.emulate() + greenlet = sys.modules['greenlet'] + except ImportError: + raise ImportError("Unable to find an implementation of greenlet.") from eventlet import greenlib, tls diff --git a/eventlet/httpc.py b/eventlet/httpc.py index 1922a41..545919c 100644 --- a/eventlet/httpc.py +++ b/eventlet/httpc.py @@ -50,7 +50,6 @@ def host_and_port_from_url(url): """ host = None port = None - #print url parsed_url = urlparse.urlparse(url) try: host, port = parsed_url[1].split(':') @@ -81,19 +80,28 @@ class HttpClient(httplib.HTTPConnection): old_putrequest = httplib.HTTPConnection.putrequest putrequest = better_putrequest - -def wrap_httplib_with_httpc(): - httplib.HTTP._connection_class = httplib.HTTPConnection = HttpClient - httplib.HTTPS._connection_class = httplib.HTTPSConnection = HttpsClient - - class HttpsClient(httplib.HTTPSConnection): + """A subclass of httplib.HTTPSConnection which works around a bug + in the interaction between eventlet sockets and httplib. httplib relies + on gc to close the socket, causing the socket to be closed too early. + + This is an awful hack and the bug should be fixed properly ASAP. + """ def close(self): pass old_putrequest = httplib.HTTPSConnection.putrequest putrequest = better_putrequest +def wrap_httplib_with_httpc(): + """Replace httplib's implementations of these classes with our enhanced ones. + + Needed to work around code that uses httplib directly.""" + httplib.HTTP._connection_class = httplib.HTTPConnection = HttpClient + httplib.HTTPS._connection_class = httplib.HTTPSConnection = HttpsClient + + + class FileScheme(object): """Retarded scheme to local file wrapper.""" host = '' @@ -161,6 +169,10 @@ class FileScheme(object): class ConnectionError(Exception): + """Detailed exception class for reporting on http connection problems. + + There are lots of subclasses so you can use closely-specified + exception clauses.""" def __init__(self, method, host, port, path, status, reason, body): self.method = method self.host = host @@ -180,6 +192,7 @@ class ConnectionError(Exception): class UnparseableResponse(ConnectionError): + """Raised when a loader cannot parse the response from the server.""" def __init__(self, content_type, response): self.content_type = content_type self.response = response @@ -193,22 +206,27 @@ class UnparseableResponse(ConnectionError): class Accepted(ConnectionError): + """ 202 Accepted """ pass class NotFound(ConnectionError): + """ 404 Not Found """ pass class Forbidden(ConnectionError): + """ 403 Forbidden """ pass class InternalServerError(ConnectionError): + """ 500 Internal Server Error """ pass class Gone(ConnectionError): + """ 410 Gone """ pass @@ -233,6 +251,12 @@ scheme_to_factory_map = { def make_connection(scheme, location, use_proxy): + """ Create a connection object to a host:port. + + @param scheme Protocol, scheme, whatever you want to call it. http, file, https are currently supported. + @param location Hostname and port number, formatted as host:port or http://host:port if you're so inclined. + @param use_proxy Connect to a proxy instead of the actual location. Uses environment variables to decide where the proxy actually lives. + """ if use_proxy: if "http_proxy" in os.environ: location = os.environ["http_proxy"] @@ -241,7 +265,7 @@ def make_connection(scheme, location, use_proxy): else: location = "localhost:3128" #default to local squid - # run a little heuristic to see if it's an url, and if so parse out the hostpart + # run a little heuristic to see if location is an url, and if so parse out the hostpart if location.startswith('http'): _scheme, location, path, parameters, query, fragment = urlparse.urlparse(location) @@ -251,11 +275,25 @@ def make_connection(scheme, location, use_proxy): def connect(url, use_proxy=False): + """ Create a connection object to the host specified in a url. Convenience function for make_connection.""" scheme, location, path, params, query, id = urlparse.urlparse(url) return make_connection(scheme, location, use_proxy) def request(connection, method, url, body='', headers=None, dumper=None, loader=None, use_proxy=False, verbose=False, ok=None): + """Make an http request to a url, for internal use mostly. + + @param connection The connection (as returned by make_connection) to use for the request. + @param method HTTP method + @param url Full url to make request on. + @param body HTTP body, if necessary for the method. Can be any object, assuming an appropriate dumper is also provided. + @param headers Dict of header name to header value + @param dumper Method that formats the body as a string. + @param loader Method that converts the response body into an object. + @param use_proxy Set to True if the connection is to a proxy. + @param verbose Set to true to change the return value of the function to: status, status_message, body + @param ok Set of valid response statuses. If the returned status is not in this list, an exception is thrown. + """ if ok is None: ok = (200, 201, 204) if headers is None: @@ -271,10 +309,12 @@ def request(connection, method, url, body='', headers=None, dumper=None, loader= if scheme == 'file': use_proxy = False - - if dumper is not None: - body = dumper(body) - headers['content-length'] = len(body) + if method in ('PUT', 'POST'): + if dumper is not None: + body = dumper(body) + # don't set content-length header because httplib does it for us in _send_request + else: + body = '' connection.request(method, url, body, headers) response = connection.getresponse() @@ -286,13 +326,11 @@ def request(connection, method, url, body='', headers=None, dumper=None, loader= body = response.read() - if loader is None: - return body - - try: - body = loader(body) - except Exception, e: - raise UnparseableResponse(loader, body) + if loader is not None: + try: + body = loader(body) + except Exception, e: + raise UnparseableResponse(loader, body) if verbose: return response.status, response.msg, body @@ -324,7 +362,7 @@ class HttpSuite(object): #import pdb; pdb.Pdb().set_trace() if headers is None: headers = {} - connection = connect(url) + connection = connect(url, use_proxy) return request(connection, 'GET', url, '', headers, None, self.loader, use_proxy, verbose, ok) def put(self, url, data, headers=None, content_type=None, verbose=False, ok=None): @@ -341,7 +379,6 @@ class HttpSuite(object): return request(connect(url), 'DELETE', url, verbose=verbose, ok=ok) def post(self, url, data='', headers=None, content_type=None, verbose=False, ok=None): - connection = connect(url) if headers is None: headers = {} if 'content-type' not in headers: @@ -353,6 +390,7 @@ class HttpSuite(object): def make_suite(dumper, loader, fallback_content_type): + """ Return a tuple of methods for making http requests with automatic bidirectional formatting with a particular content-type.""" suite = HttpSuite(dumper, loader, fallback_content_type) return suite.get, suite.put, suite.delete, suite.post diff --git a/eventlet/httpd.py b/eventlet/httpd.py index 8d078b5..61cfff9 100644 --- a/eventlet/httpd.py +++ b/eventlet/httpd.py @@ -295,7 +295,7 @@ class Request(object): typ, val, tb = sys.exc_info() body = dict(type=str(typ), error=True, reason=str(val)) self.response(response) - if type(body) is str: + if(type(body) is str and not self.response_written()): self.write(body) return try: @@ -450,7 +450,7 @@ class Server(BaseHTTPServer.HTTPServer): self.log = self def write(self, something): - sys.stdout.write('%s\n' % (something, )) + sys.stdout.write('%s' % (something, )) def log_message(self, message): self.log.write(message) @@ -463,7 +463,7 @@ class Server(BaseHTTPServer.HTTPServer): client_address, date_time, requestline, code, size, request_time """ self.log.write( - '%s - - [%s] "%s" %s %s %.6f' % args) + '%s - - [%s] "%s" %s %s %.6f\n' % args) def server(sock, site, log=None, max_size=512): diff --git a/eventlet/processes.py b/eventlet/processes.py index 8c9aea0..c51d1e7 100644 --- a/eventlet/processes.py +++ b/eventlet/processes.py @@ -42,6 +42,9 @@ class Process(object): self.command = command self.args = args self._dead_callback = dead_callback + self.run() + + def run(self): self.dead = False self.started = False self.popen4 = None diff --git a/eventlet/selecthub.py b/eventlet/selecthub.py index e9b1e41..60e2bb6 100644 --- a/eventlet/selecthub.py +++ b/eventlet/selecthub.py @@ -86,7 +86,7 @@ class Hub(object): pass if exc is not None: try: - exc() + exc(fileno) except self.runloop.SYSTEM_EXCEPTIONS: self.squelch_exception(fileno, sys.exc_info()) @@ -157,7 +157,7 @@ class Hub(object): writers = self.writers excs = self.excs try: - r, w, ig = select.select(readers, writers, [], seconds) + r, w, ig = select.select(readers.keys(), writers.keys(), [], seconds) except select.error, e: if e.args[0] == errno.EINTR: return