Esempio n. 1
0
def error_handler(exception: Exception) -> Response:
    LOGGER.exception(exception)

    http_exception = InternalServerError(str(exception))
    http_exception.__cause__ = exception

    return _handle_http_error(http_exception)
Esempio n. 2
0
async def fetch_all_user_pages(ctx: HTTPRequestContext, battletag: str, *,
                               platform="pc"):
    """
    Fetches all user pages for a specified user.

    Returns a dictionary in the format of `{"any": etree._Element | None}`.
    """
    if platform != "pc":
        coro = get_user_page(ctx, battletag, platform=platform, cache_404=True)
        result = await coro
        if isinstance(result, etree._Element):
            return {"any": result}
        else:
            # Raise a 404.
            raise NotFound()

    futures = []
    coro = get_user_page(ctx, battletag, platform=platform, cache_404=True)
    futures.append(coro)

    # Gather all the futures to download in parallel.
    results = await asyncio.gather(*futures, return_exceptions=True)
    user_data = {"any": None}
    error = None
    for key, result in zip(["any"], results):
        # Make sure it's either a None or an element.
        if isinstance(result, etree._Element):
            user_data[key] = result
        elif isinstance(result, Exception):
            logger.error("Failed to fetch user page!\n{}".format(
                ''.join(traceback.format_exception(type(result), result, result.__traceback__))
            ))
            error = result
            user_data[key] = None
        else:
            user_data[key] = None

    # Check if we should raise or return.
    if user_data["any"] is None:
        if error is not None:
            e = InternalServerError()
            e.__cause__ = error
            e.__context__ = error
            raise e
        raise NotFound()

    return user_data
Esempio n. 3
0
async def fetch_all_user_pages(ctx: HTTPRequestContext,
                               battletag: str,
                               *,
                               platform="pc"):
    """
    Fetches all user pages for a specified user.

    Returns a dictionary in the format of `{region: etree._Element | None}`.
    """
    if platform != "pc":
        coro = get_user_page(ctx,
                             battletag,
                             region="",
                             platform=platform,
                             cache_404=True)
        result = await coro
        if isinstance(result, etree._Element):
            return {"any": result, "eu": None, "us": None, "kr": None}
        else:
            # Raise a 404.
            raise NotFound()

    futures = []
    for region in AVAILABLE_REGIONS:
        # Add the get_user_page coroutine.
        coro = get_user_page(ctx,
                             battletag,
                             region=region,
                             platform=platform,
                             cache_404=True)
        futures.append(coro)

    # Gather all the futures to download paralellely.
    results = await asyncio.gather(*futures, return_exceptions=True)
    d = {"any": None}
    error = None
    for region, result in zip(AVAILABLE_REGIONS, results):
        # Remove the `/` from the front of the region.
        # This is used internally to make building the URL to get simpler.
        region = region[1:]
        # Make sure it's either a None or an element.
        if isinstance(result, etree._Element):
            d[region] = result
        elif isinstance(result, Exception):
            logger.error("Failed to fetch user page!\n{}".format(''.join(
                traceback.format_exception(type(result), result,
                                           result.__traceback__))))
            error = result
            d[region] = None
        else:
            d[region] = None

    # Check if we should raise or return.
    if not any(d[i[1:]] is not None for i in AVAILABLE_REGIONS):
        if error is not None:
            e = InternalServerError()
            e.__cause__ = error
            e.__context__ = error
            raise e
        raise NotFound()

    return d
Esempio n. 4
0
    async def process_request(self, request: Request, parent_context: Context) -> Response:
        """
        Processes a Request and returns a Response object.

        This is the main processing method of Kyoukai, and is meant to be used by one of the HTTP 
        server backends, and not by client code.

        :param request: \
            The :class:`werkzeug.wrappers.Request` object to process.
            A new :class:`~.HTTPRequestContext` will be provided to wrap this request inside of \ 
            to client code.

        :param parent_context: \
            The :class:`asphalt.core.Context` that is the parent context for this particular app. 
            It will be used as the parent for the HTTPRequestContext.

        :return: A :class:`werkzeug.wrappers.Response` object that can be written to the client \ 
            as a response.
        """
        if not self.root.finalized:
            raise RuntimeError("App was not finalized")

        # Create a new HTTPRequestContext.
        ctx = self.context_class(parent_context, request)
        ctx.app = self

        async with ctx:
            # Call match on our Blueprint to find the request.
            try:
                matched, params, rule = self.root.match(request.environ)
                ctx.rule = rule
            except NotFound as e:
                # No route matched.
                self.log_route(ctx.request, 404)
                logger.debug("Could not resolve route for {request.path}."
                             .format(request=request))
                return await self.handle_httpexception(ctx, e, request.environ)
            except MethodNotAllowed as e:
                # 405 method not allowed
                self.log_route(ctx.request, 405)
                logger.debug("Could not resolve valid method for "
                             "{request.path} ({request.method})".format(request=request))
                return await self.handle_httpexception(ctx, e, request.environ)
            except RequestRedirect as e:
                # slashes etc
                # user code is not allowed to handle this
                self.log_route(ctx.request, 301)
                return e.get_response(request.environ)
            else:
                ctx.route_matched.dispatch(ctx=ctx)

            ctx.route = matched
            ctx.bp = ctx.route.bp
            ctx.route_args = params

            result = None
            # Invoke the route.
            try:
                ctx.route_invoked.dispatch(ctx=ctx)
                # INTERCEPT
                if ctx.request.method.upper() == "OPTIONS":
                    # NO USER CODE HERE HEHEHEHEHE
                    # instead, we need to return an Allow: header
                    # kyoukai autocalcs this
                    result = Response(status=200)
                    result.headers["Allow"] = ",".join(x for x in ctx.rule.methods if x !=
                                                       "OPTIONS")
                else:
                    result = await matched.invoke(ctx, params=params)
            except BadRequestKeyError as e:
                logger.info("BadRequestKeyError: {}".format(' '.join(e.args)), exc_info=True)
                result = await self.handle_httpexception(ctx, e, request.environ)
            except HTTPException as e:
                fmtted = traceback.format_exception(type(e), e, e.__traceback__)
                logger.debug(''.join(fmtted))
                logger.info(
                    "Hit HTTPException ({}) inside function, delegating.".format(str(e))
                )
                result = await self.handle_httpexception(ctx, e, request.environ)
            except Exception as e:
                logger.exception("Unhandled exception in route function")
                new_e = InternalServerError()
                new_e.__cause__ = e
                result = await self.handle_httpexception(ctx, new_e, request.environ)
            else:
                ctx.route_completed.dispatch(ctx=ctx, result=result)
            finally:
                # result = wrap_response(result, self.response_class)
                if result:
                    # edge cases
                    self.log_route(ctx.request, result.status_code)

            # Update the Server header.
            result.headers["Server"] = "Kyoukai/{}".format(__version__)

            # list means wsgi response probably
            if not isinstance(result.response, (bytes, str, list)):
                result.set_data(str(result.response))

            result.headers["X-Powered-By"] = "Kyoukai/{}".format(__version__)

            # Return the new Response.
            return result