コード例 #1
0
ファイル: middleware.py プロジェクト: EIDA/eidaws
async def exception_handling_middleware(request, handler):
    try:
        return await handler(request)
    except (
            web.HTTPNotFound,
            web.HTTPForbidden,
            web.HTTPMethodNotAllowed,
            asyncio.CancelledError,
            FDSNHTTPError,
    ) as err:
        raise err
    except web.HTTPRequestEntityTooLarge as err:
        raise FDSNHTTPError.create(
            413,
            request,
            request_submitted=get_req_config(request, KEY_REQUEST_STARTTIME),
            error_desc_long=str(err),
            service_version=__version__,
        )
    except Exception as err:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        _logger = make_context_logger(logger, request)
        _logger.critical(f"Local Exception: {type(err)}")
        _logger.critical("Traceback information: " + repr(
            traceback.format_exception(exc_type, exc_value, exc_traceback)))
        raise FDSNHTTPError.create(
            500,
            request,
            request_submitted=get_req_config(request, KEY_REQUEST_STARTTIME),
            service_version=__version__,
        )
コード例 #2
0
    async def run(self, route, req_method="GET", context=None, **req_kwargs):
        def route_with_single_stream(route):
            streams = set()

            for se in route.stream_epochs:
                streams.add(se.id())

            return len(streams) == 1

        url = route.url
        _sorted = sorted(route.stream_epochs)

        context = context or {}
        context["chunk_size"] = self._CHUNK_SIZE
        context["stream_epochs_record"] = copy.deepcopy(_sorted)

        # context logging
        try:
            logger = make_context_logger(self._logger, *context["logger_ctx"])
        except (TypeError, KeyError):
            logger = self.logger
        finally:
            context["logger"] = logger

        with ThreadPoolExecutor(max_workers=1) as executor:
            assert route_with_single_stream(
                route
            ), "Cannot handle multiple streams within a single route."

            req_id = get_req_config(self.request, KEY_REQUEST_ID)
            async with AioSpooledTemporaryFile(
                max_size=self.config["buffer_rollover_size"],
                prefix=str(req_id) + ".",
                dir=self.config["tempdir"],
                executor=executor,
            ) as buf:

                await self._run(
                    url,
                    _sorted,
                    req_method=req_method,
                    buf=buf,
                    splitting_factor=self.config["splitting_factor"],
                    context=context,
                    **req_kwargs,
                )

                if await buf.tell():
                    async with self._lock:
                        append = self._drain.prepared or False
                        await self._flush(
                            buf,
                            self._drain,
                            context,
                            append=append,
                        )

        await self.finalize()
コード例 #3
0
    def __init__(self, request, **kwargs):
        self.request = request

        self._default_endtime = datetime.datetime.utcnow()
        self._post = False

        self._routed_urls = None
        self._response_sent = False
        self._await_on_close = [
            self._gc_response_code_stats,
        ]

        self._logger = logging.getLogger(self.LOGGER)
        self.logger = make_context_logger(self._logger, self.request)
コード例 #4
0
ファイル: process.py プロジェクト: EIDA/eidaws
    async def run(
        self,
        route,
        net,
        priority,
        req_method="GET",
        context=None,
        **req_kwargs,
    ):
        context = context or {}
        # context logging
        try:
            logger = make_context_logger(self._logger, *context["logger_ctx"])
        except (TypeError, KeyError):
            logger = self.logger
        finally:
            context["logger"] = logger

        _buffer = {}

        logger.debug(f"Fetching data for network: {net!r}")
        # granular request strategy
        tasks = [
            self._fetch(
                _route,
                parser_cb=self._parse_response,
                req_method=req_method,
                context={
                    "logger_ctx":
                    create_job_context(self.request,
                                       parent_ctx=context.get("logger_ctx"))
                },
                **req_kwargs,
            ) for _route in route
        ]
        results = await asyncio.gather(*tasks, return_exceptions=False)

        logger.debug(f"Processing data for network: {net!r}")
        for _route, data in results:
            if not data:
                continue

            se = _route.stream_epochs[0]
            _buffer[se.id()] = data

        if _buffer:
            serialized = self._dump(_buffer)
            await self._drain.drain((priority, serialized))

        await self.finalize()
コード例 #5
0
    def __init__(
        self,
        request,
        session,
        drain,
        lock=None,
        **kwargs,
    ):
        self.request = request
        self._session = session
        self._drain = drain
        self._lock = lock

        self._logger = logging.getLogger(self.LOGGER)
        self.logger = make_context_logger(self._logger, self.request)
コード例 #6
0
async def exception_handling_middleware(request, handler):
    try:
        return await handler(request)
    except (
            web.HTTPBadRequest,
            web.HTTPNoContent,
            web.HTTPNotFound,
            web.HTTPForbidden,
            web.HTTPRequestEntityTooLarge,
            web.HTTPServiceUnavailable,
            web.HTTPGatewayTimeout,
            asyncio.CancelledError,
    ) as err:
        raise err
    except Exception as err:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        _logger = make_context_logger(logger, request)
        _logger.critical(f"Local Exception: error={type(err)}, "
                         f"url={request.url!r}, method={request.method!r}")
        _logger.critical("Traceback information: " + repr(
            traceback.format_exception(exc_type, exc_value, exc_traceback)))
        raise err
コード例 #7
0
ファイル: view.py プロジェクト: EIDA/eidaws
    def __init__(self, request):
        super().__init__(request)
        self.config = self.request.config_dict[PROXY_BASE_ID]["config"]

        self._logger = logging.getLogger(self.LOGGER)
        self.logger = make_context_logger(self._logger, self.request)
コード例 #8
0
    async def run(self, route, req_method="GET", context=None, **req_kwargs):
        # context logging
        try:
            logger = make_context_logger(self._logger, *context["logger_ctx"])
        except (TypeError, KeyError):
            logger = self.logger
        finally:
            context["logger"] = logger

        req_handler = self.REQUEST_HANDLER_CLS(
            **route._asdict(),
            query_params=self.query_params,
            headers=self.request_headers,
        )

        req_handler.format = self.format
        req = getattr(req_handler, req_method.lower())(self._session)

        self._log_request(req_handler, req_method, logger=logger)
        resp_status = None
        try:
            async with req(**req_kwargs) as resp:
                resp.raise_for_status()

                resp_status = resp.status
                msg = (
                    f"Response: {resp.reason}: resp.status={resp_status}, "
                    f"resp.request_info={resp.request_info}, "
                    f"resp.url={resp.url}, resp.headers={resp.headers}"
                )
                if resp_status == 200:
                    logger.debug(msg)
                    # XXX(damb): Read the entire response into memory
                    text = await resp.read()
                    # strip header
                    data = text[(text.find(b"\n") + 1) :]
                    if data:
                        async with self._lock:
                            await self._drain.drain(data)

                elif resp_status in FDSNWS_NO_CONTENT_CODES:
                    logger.info(msg)
                else:
                    await self.handle_error(msg=msg, context=context)

        except aiohttp.ClientResponseError as err:
            resp_status = err.status
            msg = (
                f"Error while executing request: {err.message}: "
                f"error={type(err)}, resp.status={resp_status}, "
                f"resp.request_info={err.request_info}, "
                f"resp.headers={err.headers}"
            )

            if resp_status == 413:
                await self.handle_413()
            elif resp_status in FDSNWS_NO_CONTENT_CODES:
                logger.info(msg)
            # https://github.com/aio-libs/aiohttp/issues/3641
            elif (
                resp_status == 400 and "invalid constant string" == err.message
            ):
                resp_status = 204
                logger.info(
                    "Excess found in read (reset status code to "
                    f"{resp_status}). Original aiohttp error: {msg}"
                )
            else:
                await self.handle_error(msg=msg, context=context)

        except (aiohttp.ClientError, asyncio.TimeoutError) as err:
            resp_status = 503
            msg = (
                f"Error while executing request: error={type(err)}, "
                f"req_handler={req_handler!r}, method={req_method}"
            )
            if isinstance(err, aiohttp.ClientOSError):
                msg += f", errno={err.errno}"
            await self.handle_error(msg=msg, context=context)

        finally:
            if resp_status is not None:
                await self.update_cretry_budget(req_handler.url, resp_status)

            await self.finalize()
コード例 #9
0
    async def _fetch(
        self,
        route,
        parser_cb=None,
        req_method="GET",
        context=None,
        **kwargs,
    ):
        parser_cb = _coroutine_or_raise(parser_cb)
        # context logging
        try:
            logger = make_context_logger(self._logger, *context["logger_ctx"])
        except (TypeError, KeyError):
            logger = self.logger

        req_handler = self.REQUEST_HANDLER_CLS(
            **route._asdict(),
            query_params=self.query_params,
            headers=self.request_headers,
        )
        req_handler.format = self.format

        req = getattr(req_handler, req_method.lower())(self._session)

        self._log_request(req_handler, req_method, logger=logger)
        resp_status = None

        try:
            async with req(**kwargs) as resp:
                resp_status = resp.status
                resp.raise_for_status()

                msg = (
                    f"Response: {resp.reason}: resp.status={resp_status}, "
                    f"resp.request_info={resp.request_info}, "
                    f"resp.url={resp.url}, resp.headers={resp.headers}"
                )
                if resp_status != 200:
                    if resp_status in FDSNWS_NO_CONTENT_CODES:
                        logger.info(msg)
                    else:
                        await self.handle_error(msg=msg, context=context)

                    return route, None

                logger.debug(msg)
                if parser_cb is None:
                    return route, await resp.read()

                return route, await parser_cb(resp)

        except aiohttp.ClientResponseError as err:
            resp_status = err.status
            msg = (
                f"Error while executing request: {err.message}: "
                f"error={type(err)}, resp.status={resp_status}, "
                f"resp.request_info={err.request_info}, "
                f"resp.headers={err.headers}"
            )

            if resp_status == 413:
                await self.handle_413(context=context)
            elif resp_status in FDSNWS_NO_CONTENT_CODES:
                logger.info(msg)
            # https://github.com/aio-libs/aiohttp/issues/3641
            elif (
                resp_status == 400 and "invalid constant string" == err.message
            ):
                resp_status = 204
                logger.info(
                    "Excess found in read (reset status code to "
                    f"{resp_status}). Original aiohttp error: {msg}"
                )
            else:
                await self.handle_error(msg=msg, context=context)

            return route, None

        except (aiohttp.ClientError, asyncio.TimeoutError) as err:
            msg = (
                f"Error while executing request: error={type(err)}, "
                f"req_handler={req_handler!r}, method={req_method}"
            )
            if isinstance(err, aiohttp.ClientOSError):
                msg += f", errno={err.errno}"
            await self.handle_error(msg=msg, context=context)

            resp_status = 503
            return route, None
        finally:
            if resp_status is not None:
                await self.update_cretry_budget(req_handler.url, resp_status)
コード例 #10
0
ファイル: process.py プロジェクト: EIDA/eidaws
    async def run(self,
                  route,
                  net,
                  req_method="GET",
                  context=None,
                  **req_kwargs):
        context = context or {}
        context["buffer"] = {}

        # context logging
        try:
            logger = make_context_logger(self._logger, *context["logger_ctx"])
        except (TypeError, KeyError):
            logger = self.logger
        finally:
            context["logger"] = logger

        logger.debug(
            f"Fetching data for network: {net!r} (num_routes={len(route)})")

        # TODO(damb): Currently, limiting the number of concurrent connection
        # is guaranteed by sharing an aiohttp.TCPConnector instance. Though,
        # instead of limiting the connection based on a HTTP connection pool it
        # would be better to limit the overall number of tasks in order to keep
        # the memory footprint low. -> Create bound number of tasks by means of
        # globally sharing an task pool. Similar to
        # https://docs.aiohttp.org/en/stable/client_reference.html#
        # aiohttp.TCPConnector,
        # to be able to limit task creation on a) overall and b) on a per host
        # basis.

        # granular request strategy
        tasks = [
            self._fetch(
                _route,
                parser_cb=self._parse_response,
                req_method=req_method,
                context={
                    "logger_ctx":
                    create_job_context(self.request,
                                       parent_ctx=context.get("logger_ctx"))
                },
                **req_kwargs,
            ) for _route in route
        ]
        results = await asyncio.gather(*tasks, return_exceptions=False)

        logger.debug(f"Merging StationXML network element (net={net!r}, "
                     f"level={self.level!r}) ...")
        for _, station_xml in results:
            if station_xml is None:
                continue

            for net_element in station_xml.iter(STATIONXML_TAGS_NETWORK):
                self._merge_net_element(net_element,
                                        level=self.level,
                                        context=context)

        for (
                net_element,
                sta_elements,
        ) in context["buffer"].values():
            serialized = self._serialize_net_element(net_element, sta_elements)
            async with self._lock:
                await self._drain.drain(serialized)

        await self.finalize()