| 33 | |
| 34 | |
| 35 | class ErrorHandlingMiddleware(object): |
| 36 | def __init__(self, app): |
| 37 | self.app = app |
| 38 | |
| 39 | def __call__(self, environ, start_response): |
| 40 | # The middleware intercepts and handles all the errors happening down the call stack by |
| 41 | # converting them to valid HTTP responses with semantically meaningful status codes and |
| 42 | # predefined response structure (`{"faultstring": "..."}`). The earlier in the call stack is |
| 43 | # going to be run, the less unhandled errors could slip to the wsgi layer. Keep in mind that |
| 44 | # the middleware doesn't receive the headers that has been set down the call stack which |
| 45 | # means that things like CorsMiddleware and RequestIDMiddleware should be highier up the |
| 46 | # call stack to also apply to error responses. |
| 47 | try: |
| 48 | try: |
| 49 | return self.app(environ, start_response) |
| 50 | except NotFoundException: |
| 51 | raise exc.HTTPNotFound() |
| 52 | except Exception as e: |
| 53 | status = getattr(e, "code", exc.HTTPInternalServerError.code) |
| 54 | |
| 55 | if hasattr(e, "detail") and not getattr(e, "comment"): |
| 56 | setattr(e, "comment", getattr(e, "detail")) |
| 57 | |
| 58 | if hasattr(e, "body") and isinstance(getattr(e, "body", None), dict): |
| 59 | body = getattr(e, "body", None) |
| 60 | else: |
| 61 | body = {} |
| 62 | |
| 63 | if isinstance(e, exc.HTTPException): |
| 64 | status_code = status |
| 65 | message = six.text_type(e) |
| 66 | elif isinstance(e, db_exceptions.StackStormDBObjectNotFoundError): |
| 67 | status_code = exc.HTTPNotFound.code |
| 68 | message = six.text_type(e) |
| 69 | elif isinstance(e, db_exceptions.StackStormDBObjectConflictError): |
| 70 | status_code = exc.HTTPConflict.code |
| 71 | message = six.text_type(e) |
| 72 | body["conflict-id"] = getattr(e, "conflict_id", None) |
| 73 | elif isinstance(e, rbac_exceptions.AccessDeniedError): |
| 74 | status_code = exc.HTTPForbidden.code |
| 75 | message = six.text_type(e) |
| 76 | elif isinstance(e, (ValueValidationException, ValueError, ValidationError)): |
| 77 | status_code = exc.HTTPBadRequest.code |
| 78 | message = getattr(e, "message", six.text_type(e)) |
| 79 | else: |
| 80 | status_code = exc.HTTPInternalServerError.code |
| 81 | message = "Internal Server Error" |
| 82 | |
| 83 | # Log the error |
| 84 | is_internal_server_error = status_code == exc.HTTPInternalServerError.code |
| 85 | error_msg = getattr(e, "comment", six.text_type(e)) |
| 86 | extra = { |
| 87 | "exception_class": e.__class__.__name__, |
| 88 | "exception_message": six.text_type(e), |
| 89 | "exception_data": e.__dict__, |
| 90 | } |
| 91 | |
| 92 | if is_internal_server_error: |