def handle_one_request(self): self.raw_requestline = self.rfile.readline() if not self.raw_requestline: self.close_connection = 1 return if not self.parse_request(): # An error code has been sent, just exit return for prefix in self.server.wsgi_mods: if self.path.startswith(prefix): logger.debug('before wsgi SimpleHandler') try: handler = SimpleHandler(self.rfile, self.wfile, self.get_stderr(), self.get_environ(prefix)) handler.request_handler = self handler.run(self.server.wsgi_mods[prefix]) except Exception as e: logger.exception(e) logger.debug('after wsgi SimpleHandler') return mname = 'do_' + self.command if not hasattr(self, mname): self.send_error(501, "Unsupported method (%r)" % self.command) return method = getattr(self, mname) method()
def do_request(env): handler = SimpleHandler(environ=env, stdin=env['wsgi.input'], stdout=env['wsgi.input'], stderr=BytesIO()) return app(env, handler.start_response)[0].decode().strip()
def handle(self): self.raw_requestline = self.rfile.readline() if not self.parse_request(): # An error code has been sent, just exit return handler = SimpleHandler( self.rfile, self.wfile, self.get_stderr(), self.get_environ() ) handler.request_handler = self # backpointer for logging handler.run(self.server.get_app())
def get(self, path): """ get the content of a url as rendered by bottle """ handler = SimpleHandler(sys.stdin, sys.stdout, sys.stderr, {}) handler.setup_environ() env = handler.environ env.update({ "PATH_INFO": "{0}/{1}".format(self.base_url, path), "REQUEST_METHOD": "GET", }) out = b"".join(self.app(env, lambda *args: None)) return out
def testClientConnectionTerminations(self): environ = {"SERVER_PROTOCOL": "HTTP/1.0"} for exception in ( ConnectionAbortedError, BrokenPipeError, ConnectionResetError, ): with self.subTest(exception=exception): class AbortingWriter: def write(self, b): raise exception stderr = StringIO() h = SimpleHandler(BytesIO(), AbortingWriter(), stderr, environ) h.run(hello_app) self.assertFalse(stderr.getvalue())
def handle(self): """Handle a single HTTP request""" self.raw_requestline = self.rfile.readline(65537) if len(self.raw_requestline) > 65536: self.requestline = '' self.request_version = '' self.command = '' self.send_error(414) return if not self.parse_request(): # An error code has been sent, just exit return handler = SimpleHandler( self.rfile, self.wfile, self.get_stderr(), self.get_environ() ) handler.request_handler = self # backpointer for logging handler.run(self.server.get_app())
def testDontResetInternalStateOnException(self): class CustomException(ValueError): pass # We are raising CustomException here to trigger an exception # during the execution of SimpleHandler.finish_response(), so # we can easily test that the internal state of the handler is # preserved in case of an exception. class AbortingWriter: def write(self, b): raise CustomException stderr = StringIO() environ = {"SERVER_PROTOCOL": "HTTP/1.0"} h = SimpleHandler(BytesIO(), AbortingWriter(), stderr, environ) h.run(hello_app) self.assertIn("CustomException", stderr.getvalue()) # Test that the internal state of the handler is preserved. self.assertIsNotNone(h.result) self.assertIsNotNone(h.headers) self.assertIsNotNone(h.status) self.assertIsNotNone(h.environ)
def testPartialWrite(self): written = bytearray() class PartialWriter: def write(self, b): partial = b[:7] written.extend(partial) return len(partial) def flush(self): pass environ = {"SERVER_PROTOCOL": "HTTP/1.0"} h = SimpleHandler(BytesIO(), PartialWriter(), sys.stderr, environ) msg = "should not do partial writes" with self.assertWarnsRegex(DeprecationWarning, msg): h.run(hello_app) self.assertEqual( b"HTTP/1.0 200 OK\r\n" b"Content-Type: text/plain\r\n" b"Date: Mon, 05 Jun 2006 18:49:54 GMT\r\n" b"Content-Length: 13\r\n" b"\r\n" b"Hello, world!", written)
def mongrel2_handler(application, conn, debug=False): """ Based on : https://github.com/berry/Mongrel2-WSGI-Handler/blob/master/wsgi-handler.py WSGI handler based on the Python wsgiref SimpleHandler. A WSGI application should return a iterable op StringTypes. Any encoding must be handled by the WSGI application itself. """ from wsgiref.handlers import SimpleHandler try: import cStringIO as StringIO except: import StringIO # TODO - this wsgi handler executes the application and renders a page # in memory completely before returning it as a response to the client. # Thus, it does not "stream" the result back to the client. It should be # possible though. The SimpleHandler accepts file-like stream objects. So, # it should be just a matter of connecting 0MQ requests/response streams to # the SimpleHandler requests and response streams. However, the Python API # for Mongrel2 doesn't seem to support file-like stream objects for requests # and responses. Unless I have missed something. while True: if debug: print "WAITING FOR REQUEST" # receive a request req = conn.recv() if debug: print "REQUEST BODY: %r\n" % req.body if req.is_disconnect(): if debug: print "DISCONNECT" continue # effectively ignore the disconnect from the client # Set a couple of environment attributes a.k.a. header attributes # that are a must according to PEP 333 environ = req.headers environ[ 'SERVER_PROTOCOL'] = 'HTTP/1.1' # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1 environ['REQUEST_METHOD'] = environ['METHOD'] if ':' in environ['Host']: environ['SERVER_NAME'] = environ['Host'].split(':')[0] environ['SERVER_PORT'] = environ['Host'].split(':')[1] else: environ['SERVER_NAME'] = environ['Host'] environ['SERVER_PORT'] = '' environ['SCRIPT_NAME'] = '' # empty for now environ['PATH_INFO'] = urllib.unquote(environ['PATH']) if '?' in environ['URI']: environ['QUERY_STRING'] = environ['URI'].split('?')[1] else: environ['QUERY_STRING'] = '' if 'Content-Length' in environ: environ['CONTENT_LENGTH'] = environ[ 'Content-Length'] # necessary for POST to work with Django environ['wsgi.input'] = req.body if debug: print "ENVIRON: %r\n" % environ # SimpleHandler needs file-like stream objects for # requests, errors and responses reqIO = StringIO.StringIO(req.body) errIO = StringIO.StringIO() respIO = StringIO.StringIO() # execute the application handler = SimpleHandler(reqIO, respIO, errIO, environ, multithread=False, multiprocess=False) handler.run(application) # Get the response and filter out the response (=data) itself, # the response headers, # the response status code and the response status description response = respIO.getvalue() response = response.split("\r\n") data = response[-1] headers = dict([r.split(": ") for r in response[1:-2]]) code = response[0][9:12] status = response[0][13:] # strip BOM's from response data # Especially the WSGI handler from Django seems to generate them (2 actually, huh?) # a BOM isn't really necessary and cause HTML parsing errors in Chrome and Safari # See also: http://www.xs4all.nl/~mechiel/projects/bomstrip/ # Although I still find this a ugly hack, it does work. data = data.replace('\xef\xbb\xbf', '') # Get the generated errors errors = errIO.getvalue() # return the response if debug: print "RESPONSE: %r\n" % response if errors: if debug: print "ERRORS: %r" % errors data = "%s\r\n\r\n%s" % (data, errors) conn.reply_http(req, data, code=code, status=status, headers=headers)
def handleWSGI(stdin, stderr, stdout, environ): handler = SimpleHandler(stdin, stderr, stdout, environ.vars, multithread, multiprocess) handler.run_once = run_once handler.server_software = environ.vars["SERVER_SOFTWARE"] handler.run(app)
def test(app, environ={}, form={}, **kw): """Print the output of a WSGI app (e.g. for use in doctests) Runs `app` as a WSGI application and prints its output. If an untrapped error occurs in `app`, it drops into the ``pdb`` debugger's post-mortem debug shell (using ``sys.__stdout__`` if ``sys.stdout`` has been replaced). Any keyword arguments are added to the environment used to run `app`. If a keyword argument begins with ``wsgi_``, the ``_`` is replaced with a ``.``, so that you can set e.g. ``wsgi.multithread`` using a ``wsgi_multithread`` keyword argument. If a non-empty `form` dictionary is provided, it is treated as a collection of fields for a form ``POST``. The ``REQUEST_METHOD`` will default to ``POST``, and the default ``CONTENT_LENGTH``, ``CONTENT_TYPE``, and ``wsgi.input`` values will be appropriately set (but can still be overridden by explicit keyword arguments or the `environ` argument). Any `form` values that are not instances of ``basestring`` are assumed to be *sequences* of values, and will result in multiple name/value pairs being added to the encoded data sent to the application. Any WSGI-required variables that are not specified by `environ`, `form`, or keyword arguments, are initialized to default values using the ``wsgiref.util.setup_testing_defaults()`` function. """ from wsgiref.handlers import SimpleHandler from StringIO import StringIO from urllib import quote_plus environ = environ.copy() for k, v in kw.items(): if k.startswith('wsgi_'): environ[k.replace('_', '.', 1)] = v else: environ[k] = v if form: encoded = [] for k, v in form.items(): if isinstance(v, basestring): v = [v] for v in v: encoded.append('%s=%s' % (quote_plus(k), quote_plus(v))) encoded = '&'.join(encoded) environ.setdefault('wsgi.input', StringIO(encoded)) environ.setdefault('CONTENT_LENGTH', str(len(encoded))) environ.setdefault('CONTENT_TYPE', 'application/x-www-form-urlencoded') environ.setdefault('REQUEST_METHOD', 'POST') setup_testing_defaults(environ) stdout = StringIO() stderr = environ['wsgi.errors'] def wrapper(env, start): try: return app(env, start) except: stdout = sys.stdout try: if stdout is not sys.__stdout__: sys.stdout = sys.__stdout__ import pdb pdb.post_mortem(sys.exc_info()[2]) finally: sys.stdout = stdout raise SimpleHandler(environ['wsgi.input'], stdout, stderr, environ, environ['wsgi.multithread'], environ['wsgi.multiprocess']).run(wrapper) print stdout.getvalue().replace('\r\n', '\n') if stderr.getvalue(): print "--- Log Output ---" print stderr.getvalue().replace('\r\n', '\n')