From 1f8588f6691bae503f7deb3e07278f1df8f48b00 Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Fri, 13 Feb 2009 11:38:26 +0000 Subject: [PATCH] RAPI: format error messages as JSON This patch changes the format of the HTTP error messages from text/html, which is hard to parse from RAPI clients, to JSON which can be automatically parsed. The error message is an object, which contains always three keys: - code, an integer with the error code - message, a short description - explain, holding (if available) a description of the error In order to implement this, there is a bit of change to the http server and executor classes. I've tested and the error handling still works (but less optimal, no error message) in case the error formatting itself raises an exception. Reviewed-by: imsnah --- daemons/ganeti-rapi | 23 ++++++++++++++++++++++- lib/http/server.py | 27 +++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/daemons/ganeti-rapi b/daemons/ganeti-rapi index bd87b19b0..195d5c2c9 100755 --- a/daemons/ganeti-rapi +++ b/daemons/ganeti-rapi @@ -36,6 +36,7 @@ from ganeti import daemon from ganeti import ssconf from ganeti import utils from ganeti import luxi +from ganeti import serializer from ganeti.rapi import connector import ganeti.http.auth @@ -52,6 +53,24 @@ class RemoteApiRequestContext(object): self.handler_access = None +class JsonErrorRequestExecutor(http.server.HttpServerRequestExecutor): + """Custom Request Executor class that formats HTTP errors in JSON. + + """ + error_content_type = "application/json" + + def _FormatErrorMessage(self, values): + """Formats the body of an error message. + + @type values: dict + @param values: dictionary with keys code, message and explain. + @rtype: string + @return: the body of the message + + """ + return serializer.DumpJson(values, indent=True) + + class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication, http.server.HttpServer): """REST Request Handler Class. @@ -233,7 +252,9 @@ def main(): try: mainloop = daemon.Mainloop() server = RemoteApiHttpServer(mainloop, "", options.port, - ssl_params=ssl_params, ssl_verify_peer=False) + ssl_params=ssl_params, ssl_verify_peer=False, + request_executor_class= + JsonErrorRequestExecutor) server.Start() try: mainloop.Run() diff --git a/lib/http/server.py b/lib/http/server.py index d7ac0e188..b74eb3674 100644 --- a/lib/http/server.py +++ b/lib/http/server.py @@ -207,7 +207,7 @@ class _HttpClientToServerMessageReader(http.HttpMessageReader): return http.HttpClientToServerStartLine(method, path, version) -class _HttpServerRequestExecutor(object): +class HttpServerRequestExecutor(object): """Implements server side of HTTP. This class implements the server side of HTTP. It's based on code of @@ -405,8 +405,18 @@ class _HttpServerRequestExecutor(object): headers[http.HTTP_CONTENT_TYPE] = self.error_content_type self.response_msg.headers = headers - self.response_msg.body = self.error_message_format % values + self.response_msg.body = self._FormatErrorMessage(values) + def _FormatErrorMessage(self, values): + """Formats the body of an error message. + + @type values: dict + @param values: dictionary with keys code, message and explain. + @rtype: string + @return: the body of the message + + """ + return self.error_message_format % values class HttpServer(http.HttpBase): """Generic HTTP server class @@ -417,7 +427,8 @@ class HttpServer(http.HttpBase): MAX_CHILDREN = 20 def __init__(self, mainloop, local_address, port, - ssl_params=None, ssl_verify_peer=False): + ssl_params=None, ssl_verify_peer=False, + request_executor_class=None): """Initializes the HTTP server @type mainloop: ganeti.daemon.Mainloop @@ -431,10 +442,18 @@ class HttpServer(http.HttpBase): @type ssl_verify_peer: bool @param ssl_verify_peer: Whether to require client certificate and compare it with our certificate + @type request_executor_class: class + @param request_executor_class: an class derived from the + HttpServerRequestExecutor class """ http.HttpBase.__init__(self) + if request_executor_class is None: + self.request_executor = HttpServerRequestExecutor + else: + self.request_executor = request_executor_class + self.mainloop = mainloop self.local_address = local_address self.port = port @@ -505,7 +524,7 @@ class HttpServer(http.HttpBase): if pid == 0: # Child process try: - _HttpServerRequestExecutor(self, connection, client_addr) + self.request_executor(self, connection, client_addr) except Exception: logging.exception("Error while handling request from %s:%s", client_addr[0], client_addr[1]) -- GitLab