Example #1
0
    def __init__(self,
                 fallbackapp=None,
                 endpoints=None,
                 debug=False,
                 options=None,
                 default_error_handler=None):
        # Note fallback app needs to be a wsgi-compatible callable
        if fallbackapp:
            assert callable(fallbackapp), "Fallback app must be callable"
        self._fallbackapp = fallbackapp
        self._debug = debug
        if self._debug:
            _log.setLevel(logging.DEBUG)
        self._endpoints = []
        if endpoints:
            assert isinstance(endpoints, list), "endpoints must be a list"
            for ep in endpoints:
                self.add_endpoint(ep)

        if not default_error_handler:
            default_error_handler = JsonErrorHandler
        self._options = Dictomatic({
            "default_error_handler":
            default_error_handler,
        })

        if options:
            assert isinstance(options, dict), "options must be of type dict"
            self._options.update(options)
        self.verify_options()
Example #2
0
    def __init__(self, fallbackapp=None, endpoints=None,
                 debug=False, options=None, default_error_handler=None):
        # Note fallback app needs to be a wsgi-compatible callable
        if fallbackapp:
            assert callable(fallbackapp), "Fallback app must be callable"
        self._fallbackapp = fallbackapp
        self._debug = debug
        if self._debug:
            _log.setLevel(logging.DEBUG)
        self._endpoints = []
        if endpoints:
            assert isinstance(endpoints, list), "endpoints must be a list"
            for ep in endpoints:
                self.add_endpoint(ep)

        if not default_error_handler:
            default_error_handler = JsonErrorHandler
        self._options = Dictomatic({
            "default_error_handler": default_error_handler,
        })

        if options:
            assert isinstance(options, dict), "options must be of type dict"
            self._options.update(options)
        self.verify_options()
Example #3
0
 def __init__(self, req_dict):
     # Should we use dictomatic here? (tiny slowdown)
     self.req = Dictomatic.wrap(req_dict)
     self.start_time = time.time()
     self.method = self.req['REQUEST_METHOD']
     self.remote_ip = self.req['REMOTE_ADDR']
     self.body = self.req['wsgi.input'].read()
     self._buffer = ''
Example #4
0
 def __init__(self, req_dict):
     # Should we use dictomatic here? (tiny slowdown)
     self.req = Dictomatic.wrap(req_dict)
     self.start_time = time.time()
     self.method = self.req['REQUEST_METHOD']
     self.remote_ip = self.req['REMOTE_ADDR']
     self.body = self.req['wsgi.input'].read()
     self._buffer = ''
Example #5
0
 def __init__(self, req_dict):
     # Should we use dictomatic here? (tiny slowdown)
     # REVIEW: From the following items, it feels like we can remove the Dictomatic wrapper.
     self.req = Dictomatic.wrap(req_dict)
     self.start_time = time.time()
     self.method = self.req.get('REQUEST_METHOD')
     self.remote_ip = self.req.get('REMOTE_ADDR', self.req.get('HTTP_REMOTE_ADDR'))
     self.body = self.req.get('wsgi.input').read()
     self._buffer = ''
Example #6
0
    def __init__(self, fallbackapp=None, endpoints=None, \
                 debug=False, options=None, default_error_handler=None):
        self._debug = debug
        if self._debug:
            _log.setLevel(logging.DEBUG)
        self._endpoints = []
        if endpoints:
            assert isinstance(endpoints, list), "endpoints must be a list"
            for ep in endpoints:
                self.add_endpoint(ep)
        # TODO Fix fallback app here and below
        self._fallbackapp = fallbackapp

        if not default_error_handler:
            default_error_handler = JsonErrorHandler
        self._options = Dictomatic({
            "default_error_handler": default_error_handler,
        })

        if options:
            assert isinstance(options, dict), "options must be of type dict"
            self._options.update(options)
        self.verify_options()
Example #7
0
class ServicePublisher(object):
    def __init__(self,
                 fallbackapp=None,
                 endpoints=None,
                 debug=False,
                 options=None,
                 default_error_handler=None):
        # Note fallback app needs to be a wsgi-compatible callable
        if fallbackapp:
            assert callable(fallbackapp), "Fallback app must be callable"
        self._fallbackapp = fallbackapp
        self._debug = debug
        if self._debug:
            _log.setLevel(logging.DEBUG)
        self._endpoints = []
        if endpoints:
            assert isinstance(endpoints, list), "endpoints must be a list"
            for ep in endpoints:
                self.add_endpoint(ep)

        if not default_error_handler:
            default_error_handler = JsonErrorHandler
        self._options = Dictomatic({
            "default_error_handler":
            default_error_handler,
        })

        if options:
            assert isinstance(options, dict), "options must be of type dict"
            self._options.update(options)
        self.verify_options()

    def verify_options(self):
        msg = "Default exception handler "
        assert self._options.default_error_handler, msg + "must exist"
        assert isinstance(self._options.default_error_handler.code, int),\
            msg + "http code must be an int"
        assert self._options.default_error_handler.code in responses,\
            msg + "http code not in nudge.publisher.responses"
        assert isinstance(
            self._options.default_error_handler.content_type, str),\
            msg + "content_type must be a byte string"
        assert isinstance(self._options.default_error_handler.content, str),\
            msg + "content must be a byte string"
        assert isinstance(self._options.default_error_handler.headers, dict),\
            msg + "headers must be a dict"

        for k, v in self._options.default_error_handler.headers:
            assert isinstance(k, str),\
                msg+ "headers keys and values must be a byte string"
            assert isinstance(v, str),\
                msg + "headers keys and values must be a byte string"

        # Set default error params here incase of massive failure we fallback
        # to these.
        self._options.default_error_response = (
            self._options.default_error_handler.code,
            self._options.default_error_handler.content_type,
            self._options.default_error_handler.content,
            self._options.default_error_handler.headers,
        )

    def add_endpoint(self, endpoint):
        assert isinstance(endpoint, Endpoint)
        self._endpoints.append(endpoint)

    def _add_args(self, req):
        args = req.QUERY_STRING.split('=')

    def __call__(self, environ, start_response):
        '''
            This is called by each request to the server.
            This MUST return a valid HTTP response under all circumstances.

            We expect environ to be a valid wgsi python dictionary.
        '''
        req = WSGIRequest(environ)

        #        if isinstance(environ, types.DictType):
        #            req = WSGIRequest(environ)
        #        else:
        #            req = environ

        # main exception handler to ensure client gets valid response.
        # defer any mutation of the request object (incl. writes to the client)
        # until you're sure all exception prone activities have been performed
        # successfully (aka: "basic exception guarantee")
        code = None
        final_content = ""
        endpoint = None
        try:
            # allow '_method' query arg to override method
            method = req.method
            if '_method' in req.arguments:
                method = req.arguments['_method'][0].upper()
                del req.arguments['_method']

            # find appropriate endpoint
            reqline = method + urllib.unquote(req.path)
            match = None
            for endpoint in self._endpoints:
                match = endpoint.match(reqline)
                if match:
                    break

            if not match:
                if self._fallbackapp:
                    _log.debug("Using fallback app for request: (%s) (%s)" % \
                               (method, req.uri))
                    # Since recreating a stringio from the request body can be
                    # expensive, we only want to do this if we fallback.
                    environ['wsgi.input'] = StringIO.StringIO(req.body)
                    return self._fallbackapp(environ, start_response)
                else:
                    raise HTTPException(404)

            # convert all values in req.arguments from lists to scalars,
            # then combine with path args.
            arguments = dict((k, v[0]) for k, v in req.arguments.iteritems()\
                if isinstance(v, list) and len(v) > 0)
            inargs = dict(match.groupdict(), **arguments)

            # compile positional arguments
            args = []
            for arg in endpoint.sequential:
                args.append(arg.argspec(req, inargs))

            # compile keyword arguments
            kwargs = {}
            for argname, arg in endpoint.named.iteritems():
                r = arg.argspec(req, inargs)
                if r != None:
                    kwargs[argname] = r

            # invoke the service endpoint
            result = endpoint(*args, **kwargs)

            # TODO make sure this works with unicode
            _log.debug(_gen_trace_str(endpoint.function, args, kwargs, result))

            if isinstance(endpoint.renderer, RequestAwareRenderer):
                r = endpoint.renderer(req, result)
            else:
                r = endpoint.renderer(result)
            content, content_type, code, extra_headers = \
                r.content, r.content_type, r.http_status, r.headers

        except (Exception), e:
            error_response = None
            logged_trace = False
            #
            # Try to use this endpoint's exception handler(s)
            # If the raised exception is not mapped in this endpoint, or
            # this endpoint raises an exception when trying to handle,
            # we will then try to the default handler, and ultimately
            # fallback to the self._options.default_error_response, which
            # is guaranteed to be valid at app initialization.
            #
            if endpoint and endpoint.exceptions:
                try:
                    error_response = handle_exception(
                        e,
                        endpoint.exceptions,
                        default_handler=self._options.default_error_handler)
                    if not error_response:
                        raise
                except (Exception), e:
                    _log.exception("Endpoint (%s) failed to handle exception" %
                                   endpoint.name)
                    logged_trace = True

            if not error_response:
                try:
                    # Try one more time to handle a base exception
                    handler = self._options.default_error_handler()
                    error_response = handler(e)
                except (Exception), e:
                    _log.error(
                        "Default error handler failed to handle exception")
                    if logged_trace is False:
                        _log.exception(e)
Example #8
0
class ServicePublisher(object):

    def __init__(self, fallbackapp=None, endpoints=None, \
                 debug=False, options=None, default_error_handler=None):
        self._debug = debug
        if self._debug:
            _log.setLevel(logging.DEBUG)
        self._endpoints = []
        if endpoints:
            assert isinstance(endpoints, list), "endpoints must be a list"
            for ep in endpoints:
                self.add_endpoint(ep)
        # TODO Fix fallback app here and below
        self._fallbackapp = fallbackapp

        if not default_error_handler:
            default_error_handler = JsonErrorHandler
        self._options = Dictomatic({
            "default_error_handler": default_error_handler,
        })

        if options:
            assert isinstance(options, dict), "options must be of type dict"
            self._options.update(options)
        self.verify_options()

    def verify_options(self):
        msg = "Default exception handler "
        assert self._options.default_error_handler, msg + "must exist"
        assert isinstance(self._options.default_error_handler.code, int),\
            msg + "http code must be an int"
        assert self._options.default_error_handler.code in responses,\
            msg + "http code not in nudge.publisher.responses"
        assert isinstance(
            self._options.default_error_handler.content_type, str),\
            msg + "content_type must be a byte string"
        assert isinstance(self._options.default_error_handler.content, str),\
            msg + "content must be a byte string"
        assert isinstance(self._options.default_error_handler.headers, dict),\
            msg + "headers must be a dict"

        for k, v in self._options.default_error_handler.headers:
            assert isinstance(k, str),\
                msg+ "headers keys and values must be a byte string"
            assert isinstance(v, str),\
                msg + "headers keys and values must be a byte string"

        # Set default error params here incase of massive failure we fallback
        # to these.
        self._options.default_error_response = (
            self._options.default_error_handler.code,
            self._options.default_error_handler.content_type,
            self._options.default_error_handler.content,
            self._options.default_error_handler.headers,
        )

    def add_endpoint(self, endpoint):
        assert isinstance(endpoint, Endpoint)
        self._endpoints.append(endpoint)

    def _add_args(self, req):
        args = req.QUERY_STRING.split('=')

    def __call__(self, environ, start_response):
        """
            This is called by each request to the server.
            This MUST return a valid HTTP response under all circumstances.
        """
        if isinstance(environ, types.DictType):
            req = WSGIRequest(environ)
        else:
            req = environ

        # main exception handler to ensure client gets valid response.
        # defer any mutation of the request object (incl. writes to the client)
        # until you're sure all exception prone activities have been performed
        # successfully (aka: "basic exception guarantee")
        code = None
        final_content = ""
        endpoint = None
        try:
            # allow '_method' query arg to overide method
            method = req.method
            if '_method' in req.arguments:
                method = req.arguments['_method'][0].upper()
                del req.arguments['_method']

            # find appropriate endpoint
            reqline = method + urllib.unquote(req.path)
            match = None
            for endpoint in self._endpoints:
                match = endpoint.match(reqline)
                if match:
                    break

            if not match:
                # TODO: Handle HTTPException in new world exceptions
                raise HTTPException(404)
                #
                # Fallback app is untested with WSGI/EVENTLET
                # FIXTHIS!!
                #
                # if self._fallbackapp:
                    # _log.debug("falling through: %s %s" % (method, req.uri))
                    # return self._fallbackapp(event_req, start_response)
                # else:
                    # raise HTTPException(404)

            # convert all values in req.arguments from lists to scalars,
            # then combine with path args.
            arguments = dict((k, v[0]) for k, v in req.arguments.iteritems()\
                if isinstance(v, list) and len(v) > 0)
            inargs = dict(match.groupdict(), **arguments)

            # compile positional arguments
            args = []
            for arg in endpoint.sequential:
                args.append(arg.argspec(req, inargs))

            # compile keyword arguments
            kwargs = {}
            for argname, arg in endpoint.named.iteritems():
                r = arg.argspec(req, inargs)
                if r != None:
                    kwargs[argname] = r

            # invoke the service endpoint
            result = endpoint(*args, **kwargs)

            # TODO make sure this works with unicode
            _log.debug(_gen_trace_str(endpoint.function, args, kwargs, result))

            if isinstance(endpoint.renderer, RequestAwareRenderer):
                r = endpoint.renderer(req, result)
            else:
                r = endpoint.renderer(result)
            content, content_type, code, extra_headers = \
                r.content, r.content_type, r.http_status, r.headers

        except (Exception), e:
            error_response = None
            logged_trace = False
            #
            # Try to use this endpoint's exception handler(s)
            # If the raised exception is not mapped in this endpoint, or
            # this endpoint raises an exception when trying to handle,
            # we will then try to the default handler, and ultimately
            # fallback to the self._options.default_error_response, which
            # is guaranteed to be valid at app initialization.
            #
            if endpoint and endpoint.exceptions:
                try:
                    error_response = handle_exception(
                        e, endpoint.exceptions,
                        default_handler=self._options.default_error_handler
                    )
                except (Exception), e:
                    # TODO this may log too loudly
                    _log.exception(
                        "Endpoint %s failed to handle exception" % endpoint.name
                    )
                    logged_trace = True

            if not error_response:
                try:
                    # Try one more time to handle a base exception
                    handler = self._options.default_error_handler()
                    error_response = handler(e)
                except (Exception), e:
                    _log.error(
                        "Default error handler failed to handle exception")
                    if logged_trace is False:
                        _log.exception(e)