eth-stat-syncer

Cache live EVM blockchain stats
git clone git://holbrook.no/eth-stat-syncer.git
Log | Files | Refs

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'))