@@ -24,6 +24,9 @@ from nilmdb.server.serverutil import ( | |||
exception_to_httperror, | |||
CORS_allow, | |||
json_to_request_params, | |||
json_error_page, | |||
cherrypy_start, | |||
cherrypy_stop, | |||
) | |||
# Add CORS_allow tool | |||
@@ -469,70 +472,14 @@ class Server(object): | |||
def json_error_page(self, status, message, traceback, version): | |||
"""Return a custom error page in JSON so the client can parse it""" | |||
errordata = { "status" : status, | |||
"message" : message, | |||
"traceback" : traceback } | |||
# Don't send a traceback if the error was 400-499 (client's fault) | |||
try: | |||
code = int(status.split()[0]) | |||
if not self.force_traceback: | |||
if code >= 400 and code <= 499: | |||
errordata["traceback"] = "" | |||
except Exception: # pragma: no cover | |||
pass | |||
# Override the response type, which was previously set to text/html | |||
cherrypy.serving.response.headers['Content-Type'] = ( | |||
"application/json;charset=utf-8" ) | |||
# Undo the HTML escaping that cherrypy's get_error_page function applies | |||
# (cherrypy issue 1135) | |||
for k, v in errordata.iteritems(): | |||
v = v.replace("<","<") | |||
v = v.replace(">",">") | |||
v = v.replace("&","&") | |||
errordata[k] = v | |||
return json.dumps(errordata, separators=(',',':')) | |||
return json_error_page(status, message, traceback, version, | |||
self.force_traceback) | |||
def start(self, blocking = False, event = None): | |||
if not self.embedded: # pragma: no cover | |||
# Handle signals nicely | |||
if hasattr(cherrypy.engine, "signal_handler"): | |||
cherrypy.engine.signal_handler.subscribe() | |||
if hasattr(cherrypy.engine, "console_control_handler"): | |||
cherrypy.engine.console_control_handler.subscribe() | |||
# Cherrypy stupidly calls os._exit(70) when it can't bind the | |||
# port. At least try to print a reasonable error and continue | |||
# in this case, rather than just dying silently (as we would | |||
# otherwise do in embedded mode) | |||
real_exit = os._exit | |||
def fake_exit(code): # pragma: no cover | |||
if code == os.EX_SOFTWARE: | |||
fprintf(sys.stderr, "error: CherryPy called os._exit!\n") | |||
else: | |||
real_exit(code) | |||
os._exit = fake_exit | |||
cherrypy.engine.start() | |||
os._exit = real_exit | |||
# Signal that the engine has started successfully | |||
if event is not None: | |||
event.set() | |||
if blocking: | |||
try: | |||
cherrypy.engine.wait(cherrypy.engine.states.EXITING, | |||
interval = 0.1, channel = 'main') | |||
except (KeyboardInterrupt, IOError): # pragma: no cover | |||
cherrypy.engine.log('Keyboard Interrupt: shutting down bus') | |||
cherrypy.engine.exit() | |||
except SystemExit: # pragma: no cover | |||
cherrypy.engine.log('SystemExit raised: shutting down bus') | |||
cherrypy.engine.exit() | |||
raise | |||
cherrypy_start(blocking, event, self.embedded) | |||
def stop(self): | |||
cherrypy.engine.exit() | |||
cherrypy_stop() | |||
# Use a single global nilmdb.server.NilmDB and nilmdb.server.Server | |||
# instance since the database can only be opened once. For this to | |||
@@ -1,8 +1,11 @@ | |||
"""Decorators and other helpers for a CherryPy server""" | |||
"""Miscellaneous decorators and other helpers for running a CherryPy | |||
server""" | |||
import cherrypy | |||
import sys | |||
import os | |||
import decorator | |||
import simplejson as json | |||
# Decorators | |||
def chunked_response(func): | |||
@@ -122,3 +125,75 @@ def json_to_request_params(body): | |||
raise cherrypy.HTTPError(415) | |||
cherrypy.request.params.update(cherrypy.request.json) | |||
# Used as an "error_page.default" handler | |||
def json_error_page(status, message, traceback, version, | |||
force_traceback = False): | |||
"""Return a custom error page in JSON so the client can parse it""" | |||
errordata = { "status" : status, | |||
"message" : message, | |||
"traceback" : traceback } | |||
# Don't send a traceback if the error was 400-499 (client's fault) | |||
try: | |||
code = int(status.split()[0]) | |||
if not force_traceback: | |||
if code >= 400 and code <= 499: | |||
errordata["traceback"] = "" | |||
except Exception: # pragma: no cover | |||
pass | |||
# Override the response type, which was previously set to text/html | |||
cherrypy.serving.response.headers['Content-Type'] = ( | |||
"application/json;charset=utf-8" ) | |||
# Undo the HTML escaping that cherrypy's get_error_page function applies | |||
# (cherrypy issue 1135) | |||
for k, v in errordata.iteritems(): | |||
v = v.replace("<","<") | |||
v = v.replace(">",">") | |||
v = v.replace("&","&") | |||
errordata[k] = v | |||
return json.dumps(errordata, separators=(',',':')) | |||
# Start/stop CherryPy standalone server | |||
def cherrypy_start(blocking = False, event = False, embedded = False): | |||
"""Start the CherryPy server, handling errors and signals | |||
somewhat gracefully.""" | |||
if not embedded: # pragma: no cover | |||
# Handle signals nicely | |||
if hasattr(cherrypy.engine, "signal_handler"): | |||
cherrypy.engine.signal_handler.subscribe() | |||
if hasattr(cherrypy.engine, "console_control_handler"): | |||
cherrypy.engine.console_control_handler.subscribe() | |||
# Cherrypy stupidly calls os._exit(70) when it can't bind the | |||
# port. At least try to print a reasonable error and continue | |||
# in this case, rather than just dying silently (as we would | |||
# otherwise do in embedded mode) | |||
real_exit = os._exit | |||
def fake_exit(code): # pragma: no cover | |||
if code == os.EX_SOFTWARE: | |||
fprintf(sys.stderr, "error: CherryPy called os._exit!\n") | |||
else: | |||
real_exit(code) | |||
os._exit = fake_exit | |||
cherrypy.engine.start() | |||
os._exit = real_exit | |||
# Signal that the engine has started successfully | |||
if event is not None: | |||
event.set() | |||
if blocking: | |||
try: | |||
cherrypy.engine.wait(cherrypy.engine.states.EXITING, | |||
interval = 0.1, channel = 'main') | |||
except (KeyboardInterrupt, IOError): # pragma: no cover | |||
cherrypy.engine.log('Keyboard Interrupt: shutting down bus') | |||
cherrypy.engine.exit() | |||
except SystemExit: # pragma: no cover | |||
cherrypy.engine.log('SystemExit raised: shutting down bus') | |||
cherrypy.engine.exit() | |||
raise | |||
# Stop CherryPy server | |||
def cherrypy_stop(): | |||
cherrypy.engine.exit() |