[autotest] Create a DevServerHTTPError Exception class.

Cherrypy handles it's HTTPErrors by generating html and displaying
the exception message in the browsers view. To get the same exception
message in the logs, first log the actual exception then call
cherrypy.HTTPErrors. This will add a lot of traceability to
HTTPExceptions when we eventually dump the update engine logs in
autoserv.DEBUG.

TEST=Raised an exception and check that it was logged, and displayed
     in the browser view.
BUG=chromium:279299

Change-Id: I20dc817acbb770b6ab44cfea7fa97392a0c20f6b
Reviewed-on: https://chromium-review.googlesource.com/167422
Tested-by: Prashanth Balasubramanian <beeps@chromium.org>
Reviewed-by: Alejandro Deymo <deymo@chromium.org>
Reviewed-by: Chris Sosa <sosa@chromium.org>
Commit-Queue: Prashanth Balasubramanian <beeps@chromium.org>
diff --git a/devserver.py b/devserver.py
index dd02430..ded50ba 100755
--- a/devserver.py
+++ b/devserver.py
@@ -94,6 +94,17 @@
   pass
 
 
+class DevServerHTTPError(Exception):
+  """Exception class to log the HTTPResponse before routing it to cherrypy."""
+  def __init__(self, status, message):
+    """
+    @param status: HTTPResponse status.
+    @param message: Message associated with the response.
+    """
+    _Log('HTTPError status: %s message: %s', status, message)
+    raise cherrypy.HTTPError(status, message)
+
+
 def _LeadingWhiteSpaceCount(string):
   """Count the amount of leading whitespace in a string.
 
@@ -301,7 +312,7 @@
       label = label.strip()
       if label:
         return updater.HandleSetUpdatePing(ip, label)
-    raise cherrypy.HTTPError(400, 'No label provided.')
+    raise DevServerHTTPError(400, 'No label provided.')
 
 
   @cherrypy.expose
@@ -601,14 +612,13 @@
       return _PrintDocStringAsHTML(self.latestbuild)
 
     if 'target' not in params:
-      raise cherrypy.HTTPError('500 Internal Server Error',
-                               'Error: target= is required!')
+      raise DevServerHTTPError(500, 'Error: target= is required!')
     try:
       return common_util.GetLatestBuildVersion(
           updater.static_dir, params['target'],
           milestone=params.get('milestone'))
     except common_util.CommonUtilError as errmsg:
-      raise cherrypy.HTTPError('500 Internal Server Error', str(errmsg))
+      raise DevServerHTTPError(500, str(errmsg))
 
   @cherrypy.expose
   def controlfiles(self, **params):
@@ -639,8 +649,7 @@
       return _PrintDocStringAsHTML(self.controlfiles)
 
     if 'build' not in params:
-      raise cherrypy.HTTPError('500 Internal Server Error',
-                               'Error: build= is required!')
+      raise DevServerHTTPError(500, 'Error: build= is required!')
 
     if 'control_path' not in params:
       if 'suite_name' in params and params['suite_name']: