Last active
October 20, 2018 13:42
-
-
Save frennkie/836885af72be02e1ec05710f902ddbe5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import base64 | |
from http import HTTPStatus | |
from http.server import BaseHTTPRequestHandler | |
import json | |
import logging | |
from random import randint | |
import sys | |
import socketserver | |
import threading | |
import time | |
import urllib.parse | |
from infoblitz import Dashboard, Metric | |
CTYPE_HTML = "text/html" | |
CTYPE_JSON = "application/json" | |
logging.basicConfig(level=logging.DEBUG, | |
format='%(asctime)s (%(threadName)-10s) [%(name)s] ' | |
'%(levelname)s %(module)s:%(lineno)d - %(message)s', ) | |
class QuietBaseHTTPRequestHandler(BaseHTTPRequestHandler): | |
"""Quiet http request handler | |
Subclasses SimpleHTTPRequestHandler in order to overwrite the log_message | |
method, letting us reduce output generated by the handler. Only standard | |
messages are overwritten, so errors will still be displayed. | |
""" | |
def __init__(self, request, client_address, server, board=None, board_lock=None): | |
super().__init__(request, client_address, server) | |
self.board = board | |
self.board_lock = board_lock | |
def do_GET(self): | |
parts = urllib.parse.urlsplit(self.path) | |
if parts.path.endswith('/favicon.ico'): | |
ctype = 'image/x-icon' | |
content = bytes(base64.b64decode( | |
"AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAA" | |
"AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoJiIKKCYiWgAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAoJiIgKCYiuygmIhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAoJiJDKCYi7SgmIlIAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoJiJz" | |
"KCYi/SgmIqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAACgmIgooJiKmKCYi/ygmIuAoJiIOAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgmIh8oJiLPKCYi/ygm" | |
"Iv4oJiI/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAAACgmIkEoJiLrKCYi/ygmIv8oJiKMAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAACgmInAoJiL8KCYi/ygmIv8oJiL/" | |
"KCYiySgmIpwoJiJzKCYiKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgm" | |
"IhYoJiJyKCYinCgmIsIoJiL8KCYi/ygmIv8oJiL/KCYinygmIgkAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoJiJTKCYi/ygm" | |
"Iv8oJiL5KCYiaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAoJiIeKCYi7ygmIv8oJiLjKCYiNwAAAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoJiIDKCYixCgmIv8oJiK+" | |
"KCYiFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAAAAAAKCYigigmIv8oJiKJKCYiAwAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCYiPigmIvAoJiJSAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"KCYiEigmIrooJiInAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAACgmIlooJiIMAAAAAAAAAAAAAAAA" | |
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP/3" | |
"AAD/7wAA/88AAP8fAAD+PwAA/D8AAPgfAAD4DwAA/j8AAPx/AAD4/wAA" | |
"8f8AAPf/AADv/wAA//8AAA==" | |
)) | |
elif not parts.path.endswith('/'): | |
# redirect browser - doing basically what apache does | |
self.send_response(HTTPStatus.MOVED_PERMANENTLY) | |
new_parts = (parts[0], parts[1], parts[2] + '/', | |
parts[3], parts[4]) | |
new_url = urllib.parse.urlunsplit(new_parts) | |
self.send_header("Location", new_url) | |
self.end_headers() | |
return None | |
elif parts.path.endswith('/json/'): | |
ctype = CTYPE_JSON | |
content = bytes(json.dumps(json.loads('{"hello": "world"}')), "UTF-8") | |
else: | |
ctype = CTYPE_HTML | |
content = bytes("<html><head><title>RaspiBlitz Info Dashboard</title></head>", "UTF-8") | |
content += bytes("<body><h1>RaspiBlitz Info Dashboard</h1>", "UTF-8") | |
content += bytes("<p>The Dashboard Version is: v{}</p>".format(self.board.version.val), "UTF-8") | |
content += bytes("<p>The API Endpoint (JSON) is located here: <a href=\"/json/\">/json/</a></p>", "UTF-8") | |
content += bytes("</body></html>", "UTF-8") | |
self.send_response(200) | |
self.send_header("Content-type", ctype) | |
self.send_header("Content-Length", len(content)) | |
self.end_headers() | |
self.wfile.write(content) | |
def log_message(self, *args): | |
"""Overwrite so messages are logged to logging instead of STDOUT""" | |
pass | |
class ThreadedHTTPServer(object): | |
"""Runs BaseHTTPServer in a thread | |
Lets you start and stop an instance of SimpleHTTPServer. | |
""" | |
def __init__(self, host, port, board=None, board_lock=None, name=None, request_handler=QuietBaseHTTPRequestHandler): | |
"""Prepare thread and socket server | |
Creates the socket server that will use the HTTP request handler. Also | |
prepares the thread to run the serve_forever method of the socket | |
server as a daemon once it is started | |
""" | |
request_handler.board = board | |
request_handler.board_lock = board_lock | |
socketserver.TCPServer.allow_reuse_address = True | |
self.server = socketserver.TCPServer((host, port), request_handler) | |
self.server_thread = threading.Thread(name=name, target=self.server.serve_forever) | |
self.server_thread.daemon = True | |
def __enter__(self): | |
self.start() | |
return self | |
def __exit__(self, type, value, traceback): | |
self.stop() | |
def start(self): | |
"""Start the HTTP server | |
Starts the serve_forever method of Socket running the request handler | |
as a daemon thread | |
""" | |
self.server_thread.start() | |
def stop(self): | |
"""Stop the HTTP server | |
Stops the server and cleans up the port assigned to the socket | |
""" | |
self.server.shutdown() | |
self.server.server_close() | |
# Benefit of using class instead of function: Can use clean signature instead of kwargs..! | |
class DashboardPrinter(threading.Thread): | |
def __init__(self, group=None, target=None, name="DB_Printer", | |
board=None, board_lock=None, interval=None, | |
daemon=True, args=(), kwargs=None, ): | |
super().__init__(group, target, name, daemon=daemon, args=args, kwargs=kwargs) | |
self.board = board | |
self.board_lock = board_lock | |
self.interval = interval | |
def run(self): | |
while True: | |
# logging.debug("Dashboard: {} (v{})".format(board.name.val, board.version.val)) | |
with self.board_lock: | |
print("Dashboard: {} (v{})".format(self.board.name.val, self.board.version.val)) | |
time.sleep(self.interval) | |
class DashboardUpdater(threading.Thread): | |
def __init__(self, group=None, target=None, name="DB_Updater", | |
board=None, board_lock=None, interval=None, | |
daemon=True, args=(), kwargs=None, ): | |
super().__init__(group, target, name, daemon=daemon, args=args, kwargs=kwargs) | |
self.board = board | |
self.board_lock = board_lock | |
self.interval = interval | |
def run(self): | |
while True: | |
logging.debug("Updating Dashboard") | |
with self.board_lock: | |
self.board.version.val = "0.{:}".format(randint(0, 100)) | |
time.sleep(self.interval) | |
def main(): | |
host = "localhost" | |
port = 8000 | |
print_interval = 5 | |
update_interval = 10 | |
board = Dashboard("bitcoin") | |
board.timeout = 10 | |
board.interface = "eth0" | |
board.name = Metric("RaspiBlitz", style="yellow") | |
board.version = Metric("0.94", style="yellow") | |
main_thread = threading.current_thread() | |
board_lock = threading.Lock() | |
dashboard_updater_thread = DashboardUpdater(board=board, board_lock=board_lock, interval=update_interval) | |
dashboard_printer_thread = DashboardPrinter(board=board, board_lock=board_lock, interval=print_interval) | |
web_server_thread = ThreadedHTTPServer(host, port, board, board_lock, name="Web_Server") | |
logging.info("Starting Dashboard Updater") | |
dashboard_updater_thread.start() | |
logging.info("Starting Dashboard Printer") | |
dashboard_printer_thread.start() | |
logging.info("Starting Web Server: http://{}:{}".format(host, port)) | |
web_server_thread.start() | |
# for info/debug only | |
logging.debug("Listing all Threads:") | |
for t in threading.enumerate(): | |
if t is main_thread: | |
pass | |
logging.debug('Thread: %s', t.getName()) | |
try: | |
while True: # run in loop that can be interrupted with CTRL+c | |
time.sleep(1) # ToDO check.. not quite sure.. | |
except KeyboardInterrupt: | |
logging.debug("Stopping server loop") | |
web_server_thread.stop() | |
sys.exit(0) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment