http.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. #
  2. # Copyright 2018-2022 Elyra Authors
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """Mixins for Tornado handlers."""
  16. from datetime import datetime
  17. from http.client import responses
  18. import json
  19. import traceback
  20. from tornado import web
  21. class HttpErrorMixin(object):
  22. """Mixes `write_error` into tornado.web.RequestHandlers to respond with
  23. JSON-formatted errors.
  24. """
  25. def write_error(self, status_code, **kwargs):
  26. """Responds with an application/json error object.
  27. Overrides the APIHandler.write_error in the notebook server until it
  28. properly sets the 'reason' field.
  29. Parameters
  30. ----------
  31. status_code
  32. HTTP status code to set
  33. **kwargs
  34. Arbitrary keyword args. Only uses `exc_info[1]`, if it exists,
  35. to get a `log_message`, `args`, and `reason` from a raised
  36. exception that triggered this method
  37. Examples
  38. --------
  39. {"401", reason="Unauthorized", message="Invalid auth token"}
  40. """
  41. exc_info = kwargs.get("exc_info")
  42. message = ""
  43. reason = responses.get(status_code, "Unknown HTTP Error")
  44. reply = {"reason": reason, "message": message, "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
  45. if exc_info:
  46. exception = exc_info[1]
  47. # Get the custom message, if defined
  48. if isinstance(exception, web.HTTPError):
  49. reply["message"] = exception.log_message or message
  50. else:
  51. if isinstance(exception, Exception) and exception.args:
  52. if isinstance(exception.args[0], Exception):
  53. reply[
  54. "message"
  55. ] = f"Error. The server sent an invalid response.\
  56. \nPlease open an issue and provide this error message,\
  57. any error details, and any related JupyterLab log messages.\
  58. \n\nError found:\n{str(exception.args[0])}"
  59. else:
  60. reply["message"] = str(exception.args[0])
  61. else:
  62. reply["message"] = f"{exception.__class__.__name__}: {str(exception)}"
  63. reply["traceback"] = "".join(traceback.format_exception(*exc_info))
  64. # Construct the custom reason, if defined
  65. custom_reason = getattr(exception, "reason", "")
  66. if custom_reason:
  67. reply["reason"] = custom_reason
  68. self.set_header("Content-Type", "application/json")
  69. self.set_status(status_code, reason=reply["reason"])
  70. self.finish(json.dumps(reply))