server.py (4870B)
1 # standard import 2 import json 3 import os 4 import logging 5 import argparse 6 import sys 7 from http.server import ( 8 HTTPServer, 9 BaseHTTPRequestHandler, 10 ) 11 12 # external imports 13 import confini 14 from jsonrpc_std.parse import jsonrpc_from_str 15 from jsonrpc_std.interface import jsonrpc_response 16 from jsonrpc_std.error import JSONRPCException 17 18 # local imports 19 from eth_stat_syncer.store import RunStore 20 21 logging.basicConfig(level=logging.WARNING) 22 logg = logging.getLogger() 23 24 default_config_dir = os.environ.get('CONFINI_DIR', './config') 25 26 argparser = argparse.ArgumentParser() 27 argparser.add_argument('-c', '--config', dest='c', default=default_config_dir, type=str, help='rpc provider') 28 argparser.add_argument('--host', type=str, help='httpd host') 29 argparser.add_argument('--port', type=int, help='httpd port') 30 argparser.add_argument('--store-dir', dest='store_dir', type=str, help='syncerd data store base directory') 31 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') 32 argparser.add_argument('-v', action='store_true', help='be verbose') 33 argparser.add_argument('-vv', action='store_true', help='be more verbose') 34 args = argparser.parse_args() 35 36 if args.vv: 37 logging.getLogger().setLevel(logging.DEBUG) 38 elif args.v: 39 logging.getLogger().setLevel(logging.INFO) 40 41 config = confini.Config(args.c, args.env_prefix) 42 config.process() 43 # override args 44 args_override = { 45 'HTTPD_HOST': getattr(args, 'host'), 46 'HTTPD_PORT': getattr(args, 'port'), 47 'STORE_BASE_DIR': getattr(args, 'store_dir'), 48 } 49 config.dict_override(args_override, 'cli flag') 50 logg.debug('loaded config: {}\n'.format(config)) 51 52 class StatRequestHandler(BaseHTTPRequestHandler): 53 54 runstore = None 55 server_version = 'eth_stat_syncer/0.0.1' 56 57 58 def do_POST(self): 59 60 if self.headers.get('Content-Type') != 'application/json': 61 self.send_response(400, 'me read json only') 62 self.end_headers() 63 return 64 65 accept = ['application/json'] 66 try: 67 accept = self.headers.get('Accept').split(',') 68 except AttributeError: 69 pass 70 71 logg.debug('accept is {}'.format(accept)) 72 nocomprendo = True 73 if '*/*' in accept: 74 nocomprendo = False 75 elif 'application/json' in accept: 76 nocomprendo = False 77 elif '*' in accept: 78 nocomprendo = False 79 80 if nocomprendo: 81 self.send_response(400, 'me json only speak') 82 self.end_headers() 83 return 84 85 l = self.headers.get('Content-Length') 86 try: 87 l = int(l) 88 except ValueError: 89 self.send_response(400, 'content length must be integer') 90 self.end_headers() 91 return 92 if l > 4096: 93 self.send_response(400, 'too much information') 94 self.end_headers() 95 return 96 if l < 0: 97 self.send_response(400, 'you are too negative') 98 self.end_headers() 99 return 100 101 b = b'' 102 c = 0 103 while c < l: 104 d = self.rfile.read(l-c) 105 if d == None: 106 break 107 b += d 108 c += len(d) 109 if c > 4096: 110 self.send_response(413, 'i should slap you around for lying about your size') 111 self.end_headers() 112 return 113 114 o = None 115 err = None 116 try: 117 o = jsonrpc_from_str(b.decode('utf-8')) 118 except JSONRPCException as e: 119 err = e 120 121 if err != None: 122 res = e 123 self.send_response(200, 'alas with jsonrpc error') 124 125 else: 126 r = self.runstore.get('high', 'block') 127 r = int(r) 128 if r == 0: 129 r = 1 130 res = jsonrpc_response(o['id'], r) 131 logg.debug('returning {} for req {} {}'.format(r, o, self.requestline)) 132 self.send_response(200, 'It\'s a gas') 133 134 b = json.dumps(res).encode('utf-8') 135 l = len(b) 136 self.send_header('Content-Length', str(l)) 137 self.send_header('Cache-Control', 'no-cache') 138 self.send_header('Content-Type', 'application/json') 139 self.end_headers() 140 141 c = 0 142 while c < l: 143 n = self.wfile.write(b[c:]) 144 c += n 145 146 147 def run(store, host=None, port=None): 148 if host == None: 149 host = '' 150 if port == None: 151 port = 8000 152 StatRequestHandler.runstore = store 153 server_address = (host, port) 154 httpd = HTTPServer(server_address, StatRequestHandler) 155 httpd.serve_forever() 156 157 158 if __name__ == '__main__': 159 store = RunStore(basedir=config.get('STORE_BASE_DIR')) 160 run(store, host=config.get('HTTPD_HOST'), port=config.get('HTTPD_PORT'))