eth-stat-syncer/eth_stat_syncer/runnable/server.py

145 lines
4.3 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# standard import
import json
import os
import logging
import argparse
import sys
from http.server import (
HTTPServer,
BaseHTTPRequestHandler,
)
# external imports
import confini
from jsonrpc_std.parse import jsonrpc_from_str
from jsonrpc_std.interface import jsonrpc_response
# local imports
from eth_stat_syncer.store import RunStore
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
default_config_dir = os.environ.get('CONFINI_DIR', './config')
argparser = argparse.ArgumentParser()
argparser.add_argument('-c', '--config', dest='c', default=default_config_dir, type=str, help='rpc provider')
argparser.add_argument('--host', type=str, help='httpd host')
argparser.add_argument('--port', type=str, help='httpd port')
argparser.add_argument('--store-dir', dest='store_dir', type=str, help='syncerd data store base directory')
argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
argparser.add_argument('-v', action='store_true', help='be verbose')
argparser.add_argument('-vv', action='store_true', help='be more verbose')
args = argparser.parse_args()
if args.vv:
logging.getLogger().setLevel(logging.DEBUG)
elif args.v:
logging.getLogger().setLevel(logging.INFO)
config = confini.Config(args.c, args.env_prefix)
config.process()
# override args
args_override = {
'HTTPD_HOST': getattr(args, 'host'),
'HTTPD_PORT': getattr(args, 'port'),
'STORE_BASE_DIR': getattr(args, 'store_dir'),
}
config.dict_override(args_override, 'cli flag')
logg.debug('loaded config: {}\n'.format(config))
class StatRequestHandler(BaseHTTPRequestHandler):
runstore = None
server_version = 'eth_stat_syncer/0.0.1'
def do_POST(self):
if self.headers.get('Content-Type') != 'application/json':
self.send_response(400, 'me read json only')
self.end_headers()
return
if 'application/json' not in self.headers.get('Accept').split(','):
self.send_response(400, 'me json only speak')
self.end_headers()
return
l = self.headers.get('Content-Length')
try:
l = int(l)
except ValueError:
self.send_response(400, 'content length must be integer')
self.end_headers()
return
if l > 4096:
self.send_response(400, 'too much information')
self.end_headers()
return
if l < 0:
self.send_response(400, 'you are too negative')
self.end_headers()
return
b = b''
c = 0
while c < l:
d = self.rfile.read(l-c)
if d == None:
break
b += d
c += len(d)
if c > 4096:
self.send_response(413, 'i should slap you around for lying about your size')
self.end_headers()
return
o = None
err = None
try:
o = jsonrpc_from_str(b.decode('utf-8'))
except JSONRPCException as e:
err = e
if err != None:
res = e
self.send_response(200, 'alas with jsonrpc error')
else:
r = self.runstore.get('high', 'minute')
r = int(r)
if r == 0:
r = 1
res = jsonrpc_response(o['id'], r)
logg.debug('returning {} for req {} {}'.format(r, o, self.requestline))
self.send_response(200, 'It\'s a gas')
b = json.dumps(res).encode('utf-8')
l = len(b)
self.send_header('Content-Length', str(l))
self.send_header('Cache-Control', 'no-cache')
self.send_header('Content-Type', 'application/json')
self.end_headers()
c = 0
while c < l:
n = self.wfile.write(b[c:])
c += n
def run(store, host=None, port=None):
if host == None:
host = ''
if port == None:
port = 8000
StatRequestHandler.runstore = store
server_address = (host, port)
httpd = HTTPServer(server_address, StatRequestHandler)
httpd.serve_forever()
if __name__ == '__main__':
store = RunStore(basedir=config.get('STORE_BASE_DIR'))
run(store, host=config.get('HTTPD_HOST'), port=config.get('HTTPD_PORT'))