Exemple #1
0
def return_html_error(
    f: failure.Failure,
    request: Request,
    error_template: Union[str, jinja2.Template],
) -> None:
    """Sends an HTML error page corresponding to the given failure.

    Handles RedirectException and other CodeMessageExceptions (such as SynapseError)

    Args:
        f: the error to report
        request: the failing request
        error_template: the HTML template. Can be either a string (with `{code}`,
            `{msg}` placeholders), or a jinja2 template
    """
    if f.check(CodeMessageException):
        # mypy doesn't understand that f.check asserts the type.
        cme: CodeMessageException = f.value  # type: ignore
        code = cme.code
        msg = cme.msg

        if isinstance(cme, RedirectException):
            logger.info("%s redirect to %s", request, cme.location)
            request.setHeader(b"location", cme.location)
            request.cookies.extend(cme.cookies)
        elif isinstance(cme, SynapseError):
            logger.info("%s SynapseError: %s - %s", request, code, msg)
        else:
            logger.error(
                "Failed handle request %r",
                request,
                exc_info=(f.type, f.value, f.getTracebackObject()),  # type: ignore[arg-type]
            )
    elif f.check(CancelledError):
        code = HTTP_STATUS_REQUEST_CANCELLED
        msg = "Request cancelled"

        if not request._disconnected:
            logger.error(
                "Got cancellation before client disconnection when handling request %r",
                request,
                exc_info=(f.type, f.value, f.getTracebackObject()),  # type: ignore[arg-type]
            )
    else:
        code = HTTPStatus.INTERNAL_SERVER_ERROR
        msg = "Internal server error"

        logger.error(
            "Failed handle request %r",
            request,
            exc_info=(f.type, f.value, f.getTracebackObject()),  # type: ignore[arg-type]
        )

    if isinstance(error_template, str):
        body = error_template.format(code=code, msg=html.escape(msg))
    else:
        body = error_template.render(code=code, msg=msg)

    respond_with_html(request, code, body)
Exemple #2
0
def return_html_error(
    f: failure.Failure,
    request: Request,
    error_template: Union[str, jinja2.Template],
) -> None:
    """Sends an HTML error page corresponding to the given failure.

    Handles RedirectException and other CodeMessageExceptions (such as SynapseError)

    Args:
        f: the error to report
        request: the failing request
        error_template: the HTML template. Can be either a string (with `{code}`,
            `{msg}` placeholders), or a jinja2 template
    """
    if f.check(CodeMessageException):
        cme = f.value
        code = cme.code
        msg = cme.msg

        if isinstance(cme, RedirectException):
            logger.info("%s redirect to %s", request, cme.location)
            request.setHeader(b"location", cme.location)
            request.cookies.extend(cme.cookies)
        elif isinstance(cme, SynapseError):
            logger.info("%s SynapseError: %s - %s", request, code, msg)
        else:
            logger.error(
                "Failed handle request %r",
                request,
                exc_info=(f.type, f.value, f.getTracebackObject()),
            )
    else:
        code = http.HTTPStatus.INTERNAL_SERVER_ERROR
        msg = "Internal server error"

        logger.error(
            "Failed handle request %r",
            request,
            exc_info=(f.type, f.value, f.getTracebackObject()),
        )

    if isinstance(error_template, str):
        body = error_template.format(code=code, msg=html.escape(msg))
    else:
        body = error_template.render(code=code, msg=msg)

    body_bytes = body.encode("utf-8")
    request.setResponseCode(code)
    request.setHeader(b"Content-Type", b"text/html; charset=utf-8")
    request.setHeader(b"Content-Length", b"%i" % (len(body_bytes), ))
    request.write(body_bytes)
    finish_request(request)
Exemple #3
0
def return_json_error(f: failure.Failure, request: SynapseRequest) -> None:
    """Sends a JSON error response to clients."""

    if f.check(SynapseError):
        # mypy doesn't understand that f.check asserts the type.
        exc: SynapseError = f.value  # type: ignore
        error_code = exc.code
        error_dict = exc.error_dict()

        logger.info("%s SynapseError: %s - %s", request, error_code, exc.msg)
    elif f.check(CancelledError):
        error_code = HTTP_STATUS_REQUEST_CANCELLED
        error_dict = {"error": "Request cancelled", "errcode": Codes.UNKNOWN}

        if not request._disconnected:
            logger.error(
                "Got cancellation before client disconnection from %r: %r",
                request.request_metrics.name,
                request,
                exc_info=(f.type, f.value,
                          f.getTracebackObject()),  # type: ignore[arg-type]
            )
    else:
        error_code = 500
        error_dict = {
            "error": "Internal server error",
            "errcode": Codes.UNKNOWN
        }

        logger.error(
            "Failed handle request via %r: %r",
            request.request_metrics.name,
            request,
            exc_info=(f.type, f.value,
                      f.getTracebackObject()),  # type: ignore[arg-type]
        )

    # Only respond with an error response if we haven't already started writing,
    # otherwise lets just kill the connection
    if request.startedWriting:
        if request.transport:
            try:
                request.transport.abortConnection()
            except Exception:
                # abortConnection throws if the connection is already closed
                pass
    else:
        respond_with_json(
            request,
            error_code,
            error_dict,
            send_cors=True,
        )
Exemple #4
0
    async def _purge_history(
        self, purge_id: str, room_id: str, token: str, delete_local_events: bool
    ) -> None:
        """Carry out a history purge on a room.

        Args:
            purge_id: The id for this purge
            room_id: The room to purge from
            token: topological token to delete events before
            delete_local_events: True to delete local events as well as remote ones
        """
        self._purges_in_progress_by_room.add(room_id)
        try:
            with await self.pagination_lock.write(room_id):
                await self.storage.purge_events.purge_history(
                    room_id, token, delete_local_events
                )
            logger.info("[purge] complete")
            self._purges_by_id[purge_id].status = PurgeStatus.STATUS_COMPLETE
        except Exception:
            f = Failure()
            logger.error(
                "[purge] failed", exc_info=(f.type, f.value, f.getTracebackObject())
            )
            self._purges_by_id[purge_id].status = PurgeStatus.STATUS_FAILED
        finally:
            self._purges_in_progress_by_room.discard(room_id)

            # remove the purge from the list 24 hours after it completes
            def clear_purge():
                del self._purges_by_id[purge_id]

            self.hs.get_reactor().callLater(24 * 3600, clear_purge)
Exemple #5
0
 def get_error_html(self, status_code, **kwargs):
     try:
         if 'exception' in kwargs:
             failure = kwargs.get('exception')
             if isinstance(failure, cyclone.web.HTTPError):
                 failure = Failure(failure)
             logger.exception(failure)
             if os.environ.get('XDEBUG'):
                 from mako import exceptions
                 return exceptions.html_error_template().render(
                     traceback=failure.getTracebackObject())
         if self.request.headers.get(
                 'X-Requested-With') == 'XMLHttpRequest':
             return self.render_json(code=1,
                                     msg=u'%s:服务器处理失败,请联系管理员' % status_code)
         if status_code == 404:
             return self.render_error(msg=u'404:页面不存在')
         if status_code == 403:
             return self.render_error(msg=u'403:非法的请求')
         if status_code == 500:
             return self.render_error(msg=u'500:服务器处理失败,请联系管理员')
         return self.render_error(msg=u'%s:服务器处理失败,请联系管理员' % status_code)
     except Exception as err:
         logger.exception(err)
         return self.render_error(msg=u'%s:服务器处理失败,请联系管理员' % status_code)
Exemple #6
0
    def run():
        with _bg_metrics_lock:
            count = _background_process_counts.get(desc, 0)
            _background_process_counts[desc] = count + 1

        _background_process_start_count.labels(desc).inc()
        _background_process_in_flight_count.labels(desc).inc()

        with BackgroundProcessLoggingContext(desc) as context:
            context.request = "%s-%i" % (desc, count)

            try:
                result = func(*args, **kwargs)

                # We probably don't have an ensureDeferred in our call stack to handle
                # coroutine results, so we need to ensureDeferred here.
                #
                # But we need this check because ensureDeferred doesn't like being
                # called on immediate values (as opposed to Deferreds or coroutines).
                if iscoroutine(result):
                    result = defer.ensureDeferred(result)

                return (yield result)
            except Exception:
                # failure.Failure() fishes the original Failure out of our stack, and
                # thus gives us a sensible stack trace.
                f = Failure()
                logger.error(
                    "Background process '%s' threw an exception",
                    desc,
                    exc_info=(f.type, f.value, f.getTracebackObject()),
                )
            finally:
                _background_process_in_flight_count.labels(desc).dec()
Exemple #7
0
def log_failure(failure: Failure,
                msg: str,
                consumeErrors: bool = True) -> Optional[Failure]:
    """Creates a function suitable for passing to `Deferred.addErrback` that
    logs any failures that occur.

    Args:
        failure: The Failure to log
        msg: Message to log
        consumeErrors: If true consumes the failure, otherwise passes on down
            the callback chain

    Returns:
        The Failure if consumeErrors is false. None, otherwise.
    """

    logger.error(
        msg,
        exc_info=(failure.type, failure.value, failure.getTracebackObject()
                  )  # type: ignore[arg-type]
    )

    if not consumeErrors:
        return failure
    return None
Exemple #8
0
def return_json_error(f: failure.Failure, request: SynapseRequest) -> None:
    """Sends a JSON error response to clients.
    """

    if f.check(SynapseError):
        error_code = f.value.code
        error_dict = f.value.error_dict()

        logger.info("%s SynapseError: %s - %s", request, error_code, f.value.msg)
    else:
        error_code = 500
        error_dict = {"error": "Internal server error", "errcode": Codes.UNKNOWN}

        logger.error(
            "Failed handle request via %r: %r",
            request.request_metrics.name,
            request,
            exc_info=(f.type, f.value, f.getTracebackObject()),
        )

    # Only respond with an error response if we haven't already started writing,
    # otherwise lets just kill the connection
    if request.startedWriting:
        if request.transport:
            try:
                request.transport.abortConnection()
            except Exception:
                # abortConnection throws if the connection is already closed
                pass
    else:
        respond_with_json(
            request, error_code, error_dict, send_cors=True,
        )
Exemple #9
0
    def _purge_history(self, purge_id, room_id, token,
                       delete_local_events):
        """Carry out a history purge on a room.

        Args:
            purge_id (str): The id for this purge
            room_id (str): The room to purge from
            token (str): topological token to delete events before
            delete_local_events (bool): True to delete local events as well as
                remote ones

        Returns:
            Deferred
        """
        self._purges_in_progress_by_room.add(room_id)
        try:
            with (yield self.pagination_lock.write(room_id)):
                yield self.store.purge_history(
                    room_id, token, delete_local_events,
                )
            logger.info("[purge] complete")
            self._purges_by_id[purge_id].status = PurgeStatus.STATUS_COMPLETE
        except Exception:
            f = Failure()
            logger.error(
                "[purge] failed",
                exc_info=(f.type, f.value, f.getTracebackObject()),
            )
            self._purges_by_id[purge_id].status = PurgeStatus.STATUS_FAILED
        finally:
            self._purges_in_progress_by_room.discard(room_id)

            # remove the purge from the list 24 hours after it completes
            def clear_purge():
                del self._purges_by_id[purge_id]
            self.hs.get_reactor().callLater(24 * 3600, clear_purge)
Exemple #10
0
    async def _shutdown_and_purge_room(
        self,
        delete_id: str,
        room_id: str,
        requester_user_id: str,
        new_room_user_id: Optional[str] = None,
        new_room_name: Optional[str] = None,
        message: Optional[str] = None,
        block: bool = False,
        purge: bool = True,
        force_purge: bool = False,
    ) -> None:
        """
        Shuts down and purges a room.

        See `RoomShutdownHandler.shutdown_room` for details of creation of the new room

        Args:
            delete_id: The ID for this delete.
            room_id: The ID of the room to shut down.
            requester_user_id:
                User who requested the action. Will be recorded as putting the room on the
                blocking list.
            new_room_user_id:
                If set, a new room will be created with this user ID
                as the creator and admin, and all users in the old room will be
                moved into that room. If not set, no new room will be created
                and the users will just be removed from the old room.
            new_room_name:
                A string representing the name of the room that new users will
                be invited to. Defaults to `Content Violation Notification`
            message:
                A string containing the first message that will be sent as
                `new_room_user_id` in the new room. Ideally this will clearly
                convey why the original room was shut down.
                Defaults to `Sharing illegal content on this server is not
                permitted and rooms in violation will be blocked.`
            block:
                If set to `true`, this room will be added to a blocking list,
                preventing future attempts to join the room. Defaults to `false`.
            purge:
                If set to `true`, purge the given room from the database.
            force_purge:
                If set to `true`, the room will be purged from database
                also if it fails to remove some users from room.

        Saves a `RoomShutdownHandler.ShutdownRoomResponse` in `DeleteStatus`:
        """

        self._purges_in_progress_by_room.add(room_id)
        try:
            with await self.pagination_lock.write(room_id):
                self._delete_by_id[
                    delete_id].status = DeleteStatus.STATUS_SHUTTING_DOWN
                self._delete_by_id[
                    delete_id].shutdown_room = await self._room_shutdown_handler.shutdown_room(
                        room_id=room_id,
                        requester_user_id=requester_user_id,
                        new_room_user_id=new_room_user_id,
                        new_room_name=new_room_name,
                        message=message,
                        block=block,
                    )
                self._delete_by_id[
                    delete_id].status = DeleteStatus.STATUS_PURGING

                if purge:
                    logger.info("starting purge room_id %s", room_id)

                    # first check that we have no users in this room
                    if not force_purge:
                        joined = await self.store.is_host_joined(
                            room_id, self._server_name)
                        if joined:
                            raise SynapseError(
                                400, "Users are still joined to this room")

                    await self.storage.purge_events.purge_room(room_id)

            logger.info("complete")
            self._delete_by_id[delete_id].status = DeleteStatus.STATUS_COMPLETE
        except Exception:
            f = Failure()
            logger.error(
                "failed",
                exc_info=(f.type, f.value,
                          f.getTracebackObject()),  # type: ignore
            )
            self._delete_by_id[delete_id].status = DeleteStatus.STATUS_FAILED
            self._delete_by_id[delete_id].error = f.getErrorMessage()
        finally:
            self._purges_in_progress_by_room.discard(room_id)

            # remove the delete from the list 24 hours after it completes
            def clear_delete() -> None:
                del self._delete_by_id[delete_id]
                self._delete_by_room[room_id].remove(delete_id)
                if not self._delete_by_room[room_id]:
                    del self._delete_by_room[room_id]

            self.hs.get_reactor().callLater(
                PaginationHandler.CLEAR_PURGE_AFTER_MS / 1000, clear_delete)