# Copyright (c) The PyAMF Project. # See LICENSE.txt for details. """ WSGI server implementation. The Python Web Server Gateway Interface (WSGI) is a simple and universal interface between web servers and web applications or frameworks. The WSGI interface has two sides: the "server" or "gateway" side, and the "application" or "framework" side. The server side invokes a callable object (usually a function or a method) that is provided by the application side. Additionally WSGI provides middlewares; a WSGI middleware implements both sides of the API, so that it can be inserted "between" a WSGI server and a WSGI application -- the middleware will act as an application from the server's point of view, and as a server from the application's point of view. @see: U{WSGI homepage (external)} @see: U{PEP-333 (external)} @since: 0.1.0 """ import pyamf from pyamf import remoting from pyamf.remoting import gateway __all__ = ['WSGIGateway'] class WSGIGateway(gateway.BaseGateway): """ WSGI Remoting Gateway. """ def getResponse(self, request, environ): """ Processes the AMF request, returning an AMF response. @param request: The AMF Request. @type request: L{Envelope} @rtype: L{Envelope} @return: The AMF Response. """ response = remoting.Envelope(request.amfVersion) for name, message in request: processor = self.getProcessor(message) environ['pyamf.request'] = message response[name] = processor(message, http_request=environ) return response def badRequestMethod(self, environ, start_response): """ Return HTTP 400 Bad Request. """ response = "400 Bad Request\n\nTo access this PyAMF gateway you " \ "must use POST requests (%s received)" % environ['REQUEST_METHOD'] start_response('400 Bad Request', [ ('Content-Type', 'text/plain'), ('Content-Length', str(len(response))), ('Server', gateway.SERVER_NAME), ]) return [response] def __call__(self, environ, start_response): """ @rtype: C{StringIO} @return: File-like object. """ if environ['REQUEST_METHOD'] != 'POST': return self.badRequestMethod(environ, start_response) body = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])) stream = None timezone_offset = self._get_timezone_offset() # Decode the request try: request = remoting.decode(body, strict=self.strict, logger=self.logger, timezone_offset=timezone_offset) except (pyamf.DecodeError, IOError): if self.logger: self.logger.exception('Error decoding AMF request') response = "400 Bad Request\n\nThe request body was unable to " \ "be successfully decoded." if self.debug: response += "\n\nTraceback:\n\n%s" % gateway.format_exception() start_response('400 Bad Request', [ ('Content-Type', 'text/plain'), ('Content-Length', str(len(response))), ('Server', gateway.SERVER_NAME), ]) return [response] except (KeyboardInterrupt, SystemExit): raise except: if self.logger: self.logger.exception('Unexpected error decoding AMF request') response = ("500 Internal Server Error\n\nAn unexpected error " "occurred whilst decoding.") if self.debug: response += "\n\nTraceback:\n\n%s" % gateway.format_exception() start_response('500 Internal Server Error', [ ('Content-Type', 'text/plain'), ('Content-Length', str(len(response))), ('Server', gateway.SERVER_NAME), ]) return [response] if self.logger: self.logger.debug("AMF Request: %r" % request) # Process the request try: response = self.getResponse(request, environ) except (KeyboardInterrupt, SystemExit): raise except: if self.logger: self.logger.exception('Error processing AMF request') response = ("500 Internal Server Error\n\nThe request was " "unable to be successfully processed.") if self.debug: response += "\n\nTraceback:\n\n%s" % gateway.format_exception() start_response('500 Internal Server Error', [ ('Content-Type', 'text/plain'), ('Content-Length', str(len(response))), ('Server', gateway.SERVER_NAME), ]) return [response] if self.logger: self.logger.debug("AMF Response: %r" % response) # Encode the response try: stream = remoting.encode(response, strict=self.strict, timezone_offset=timezone_offset) except: if self.logger: self.logger.exception('Error encoding AMF request') response = ("500 Internal Server Error\n\nThe request was " "unable to be encoded.") if self.debug: response += "\n\nTraceback:\n\n%s" % gateway.format_exception() start_response('500 Internal Server Error', [ ('Content-Type', 'text/plain'), ('Content-Length', str(len(response))), ('Server', gateway.SERVER_NAME), ]) return [response] response = stream.getvalue() start_response('200 OK', [ ('Content-Type', remoting.CONTENT_TYPE), ('Content-Length', str(len(response))), ('Server', gateway.SERVER_NAME), ]) return [response]