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)