Exemple #1
0
def serve_static_file(path, content_type=None, bufsize=8192, **kwargs):
    """
    Serve a static file located at ``path``.

    :returns: A :class:`~fresco.response.Response` object for the file at
              ``path``
    """

    try:
        mtime = os.path.getmtime(path)
    except OSError:
        return Response.not_found()

    request = context.request
    mod_since = request.get_header('if-modified-since')
    if mod_since is not None:
        try:
            mod_since = mktime_tz(parsedate_tz(mod_since))  # type: ignore
        except (TypeError, OverflowError, ValueError):
            return Response.bad_request()
        if int(mtime) <= int(mod_since):
            return Response(status=STATUS_NOT_MODIFIED)

    if content_type is None:
        content_type = mimetypes.guess_type(path)[0]
        if content_type is None:
            content_type = 'application/octet-stream'

    file_wrapper = request.environ.get('wsgi.file_wrapper')
    if file_wrapper is not None:

        def content_iterator(f):
            return file_wrapper(f, bufsize)
    else:

        def content_iterator(f):
            return ClosingIterator(iter(lambda: f.read(bufsize), b''), f.close)

    try:
        _file = open(path, 'rb')
    except IOError:
        return Response.forbidden()

    return Response(status=STATUS_OK,
                    content_length=str(os.path.getsize(path)),
                    last_modified=formatdate(mtime,
                                             localtime=False,
                                             usegmt=True),
                    content_type=content_type,
                    content=content_iterator(_file),
                    passthrough=True,
                    **kwargs)
    def view_method_not_found(self, valid_methods):
        """
        Return a ``405 Method Not Allowed`` response.

        Called when a view matched the pattern but no HTTP methods matched.
        """
        return Response.method_not_allowed(valid_methods)
class Unauthorized(ResponseException):
    """\
    Return a 401 Unauthorized response.

    Use this when you want to flag that the user does not have permission to
    access a resource but may be able to gain access, eg by logging in.
    """
    response = Response(['Unauthorized'], status=401)
class Forbidden(ResponseException):
    """\
    Return a 403 Forbidden response

    Use this when you want to flag that the user does not have permission to
    access a resource and that there is no possiblity to do so (eg they are
    already logged in, but their account does not have the right access
    permissions)
    """
    response = Response(['Forbidden'], status=403)
class TooManyRequests(ResponseException):
    """
    Indicates that the user has sent too many
    requests in a given amount of time ("rate limiting").

    The response representations SHOULD include details explaining the
    condition, and MAY include a Retry-After header indicating how long
    to wait before making a new request.
    """
    response = Response(['Too many requests'], status=429)
class NotFound(ResponseException):
    """\
    Raised when a view needs to signal a 404 not found condition.
    """
    response = Response.not_found()

    def __init__(self, content=None, **kwargs):
        """
        ``final`` is false by default, meaning other routes will be offered the
        opportunity to handle the request. Only after other routes have been
        tried will an error response be returned.
        """
        kwargs.setdefault('final', False)
        super(NotFound, self).__init__(content, **kwargs)
class ResponseException(Exception):
    """\
    An exception class with an associated :class:`fresco.response.Response`
    instance that will render a suitable error page.
    """
    response = Response()

    #: If true, the error response will be returned immediately without trying
    #: any other routes.
    is_final = True

    def __init__(self, content=None, **kwargs):
        self.is_final = kwargs.pop('final', True)
        if content:
            kwargs['content'] = content
        if kwargs:
            self.response = self.response.replace(**kwargs)
    def handle_exception(self,
                         request,
                         allow_reraise=True) -> Union[
                             Response,
                             Tuple[Type[BaseException],
                                   BaseException,
                                   types.TracebackType]]:

        exc_info = sys.exc_info()
        if exc_info[0] is None:
            raise AssertionError("handle_exception called "
                                 "when no exception is being handled")

        have_error_handlers = self.process_exception_handlers or any(
            st in (None, 500)
            for st, fn in self.process_http_error_response_handlers)

        # Backwards compatibility: if no exception or 500 http error
        # handlers have been installed we default to the old behavior
        # of raising the exception and letting the upstream
        # server handle it
        if allow_reraise and not have_error_handlers:
            raise exc_info[1].with_traceback(exc_info[2])  # type: ignore
        response: Union[
            Response,
            Tuple[Type[BaseException], BaseException, types.TracebackType]
        ] = Response.internal_server_error()

        if not self.process_exception_handlers:
            self.log_exception(request, exc_info)
        for exc_type, func in self.process_exception_handlers:
            try:
                if not issubclass(exc_info[0], exc_type):  # type: ignore
                    continue
                r = func(request, exc_info)
                if r is not None:
                    response = r
                    break
            except Exception:
                self.log_exception(request)
        if isinstance(response, tuple) and len(response) == 3:
            raise exc_info[1].with_traceback(exc_info[2])  # type: ignore
        return response
    def get_response(
            self, routecollection=None, request=None, path=None, method=None,
            currentcontext=context.currentcontext,
            normpath=normpath):

        ctx = currentcontext()
        try:
            routecollection = routecollection or self
            request = request or ctx['request']
            method = method or request.method
            path = path or request.path_info
            error_response = None

            ctx['app'] = self
            environ = request.environ
            environ['fresco.app'] = self

            if path:
                if ('..' in path or '//' in path or '/./' in path):
                    path = normpath(path)
            else:
                path = '/'
        except ResponseException as e:
            return e.response

        response = None
        for f in self.process_request_handlers:
            try:
                r = f(request)
                if r is not None:
                    response = r
            except Exception:
                return self.handle_exception(request,
                                             allow_reraise=False)
        if response:
            return response

        try:
            for traversal in routecollection.get_routes(path, method, request):
                try:
                    route = traversal.route
                    environ['wsgiorg.routing_args'] = (traversal.args,
                                                       traversal.kwargs)
                    view = route.getview(method)
                    ctx['view_self'] = getattr(view, '__self__', None)
                    ctx['route_traversal'] = traversal
                    if self.logger:
                        self.logger.info("matched route: %s %r => %r",
                                         request.method, path, fq_path(view))

                    response = None
                    for f in self.process_view_handlers:
                        try:
                            r = f(request, view, traversal.args,
                                  traversal.kwargs)
                            if r is not None:
                                response = r
                        except Exception:
                            return self.handle_exception(request,
                                                         allow_reraise=False)
                    if response is not None:
                        if isinstance(response, Response):
                            return response
                        else:
                            view = response

                    view = route.getdecoratedview(view)
                    response = view(*traversal.args, **traversal.kwargs)
                    if route.fallthrough_statuses and \
                       response.status_code in route.fallthrough_statuses:
                        continue

                except ResponseException as e:
                    if e.is_final:
                        return e.response
                    error_response = error_response or e.response
                    if route.fallthrough_statuses and \
                       error_response.status_code in route.fallthrough_statuses:
                        continue

                except Exception:
                    return self.handle_exception(request)
                else:
                    return response

        except ResponseException as e:
            if e.is_final:
                return e.response
            error_response = error_response or e.response

        except Exception:
            return self.handle_exception(request)

        # A route was matched, but an error was returned
        if error_response:
            return error_response

        # Is this a head request?
        if method == 'HEAD':
            response = self.get_response(routecollection, request, path, GET)
            if '200' <= response.status < '300':
                return response.replace(content=[], content_length=0)
            return response

        # Is the URL matched by another HTTP method?
        methods = self.get_methods(routecollection, request, path)
        if methods:
            return Response.method_not_allowed(methods)

        # Is the URL just missing a trailing '/'?
        if path[-1] != '/':
            for _ in self.get_methods(routecollection, request, path + '/'):
                return Response.redirect_permanent(path + '/')

        return Response.not_found()
 def __init__(self, *args, **kwargs):
     self.response = Response.redirect(*args, **kwargs)
class BadRequest(ResponseException):
    """\
    Return a 400 Bad Request response
    """
    response = Response(['Bad request'], status=400)
 def __init__(self, valid_methods, **kwargs):
     self.response = Response.method_not_allowed(valid_methods)
     super(MethodNotAllowed, self).__init__(**kwargs)