def serve(self): """ Listen for http requests forever and trigger processing. """ try: c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) c.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) c.bind(('localhost', self.port)) c.listen(1) except socket.error as msg: log(0, msg) return log(0, "Server running on http://{}:{} .".format('localhost', self.port)) while 1: csock, caddr = c.accept() conn = csock.makefile(mode='rwb', buffering=1) self.request = Request() self.response = Response(conn, self) if self.request.parse(conn): self.request.origin = caddr[0] log(2, "Request: %s" % self.request) try: # preprocessing (middlewares) for m in self.middlewares: m.process_request(self.request, self.response) # processing (check registered routes for actions) processed = False for route in self.routes: log(2, "Matche %s gegen %s" % (route[0], self.request.path)) match = re.match(route[0], self.request.path) if match: if not route[2] or self.request.method.lower() in [x.lower() for x in route[2]]: log(2, "Route {} und Methode {} matcht Request {}".format(route[0], self.request.method, self.request.path)) route[1](self.request, self.response, match) processed = True break if not processed: raise StopProcessing(404, "No matching route.") except StopProcessing as spe: self.response.send(code=spe.code, body=spe.reason) # preprocessing (middlewares) for m in self.middlewares: m.process_response(self.response) # actually write response to server connection try: self.response.commit() except (ConnectionAbortedError, BrokenPipeError): pass conn.close() csock.close()
def commit(self): def w(txt): """Decode as UTF-8 and write to client connection""" self.conn.write(bytes(txt, 'UTF-8')) # TT: blatt6 - default values have been moved to constructor # TT: to make them available to logging from server.statuscodes import statuscode (phrase, explanation) = statuscode(self.code) w("HTTP/1.1 %d %s\n" % (self.code, phrase)) log(1, "HTTP/1.1 %d %s (%s)\n" % (self.code, phrase, explanation)) import datetime w("Date: %s\n" % datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S")) w("Connection: close\n") # send cookies for c in self.cookies: w(c.get_header()) # send other headers for key, value in self.headers.items(): w("%s: %s\n" % (key, value)) w("\n") # extra leerzeile schliesst header ab if self.body: if isinstance(self.body, str): # UTF-8 w(self.body) else: # bytes, e.g. binary data self.conn.write(self.body)
async def send_transaction(self, wif, address_from, address_to, amount, txid, vout): io = await self.transaction_io(txid, vout, amount, address_from, address_to) if io["inputs"] is None: return io else: outputs = io["outputs"] inputs = io["inputs"] # creates a replaceable transaction. tx = await self.call("createrawtransaction", [inputs, outputs]) if tx["error"] is None: signed = await self.call("signrawtransactionwithkey", [tx["result"], [wif]]) if signed["error"] is None: sent = await self.call("sendrawtransaction", [signed["result"]["hex"], 0]) if sent["result"] is not None: log(f"created tx {sent['result']}") return self.parse_response(sent) else: return self.parse_response(signed) else: return self.parse_response(tx)
def sendfile(self, request, response, pathmatch=None): """Serve a static file from local filesystem.""" # path might be urlencoded from urllib.parse import unquote resource = unquote(pathmatch.group('file')) # directory traversal protection # os function os.path.abspath calculates normalized absolute path # self.path must be a prefix of it from os.path import abspath log( 2, "check for directory traversal attack: does %s start with %s ?" % (abspath(self.path + resource), abspath(self.path))) if not abspath(self.path + resource).startswith(abspath(self.path)): raise StopProcessing( 500, "500 internal server error.\nDirectory traversal attack attempted.\n" ) # open, read and server file try: log(2, "Try to open %s" % resource) with open(self.path + resource, 'rb') as f: # guess type from extension import mimetypes (content_type, encoding) = mimetypes.guess_type(resource) response.set_content_type(content_type) response.send(body=f.read()) # read and dump whole file except IOError: raise StopProcessing(404, "File not found: %s" % resource)
def generate(): generated = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1) private = generated.to_string() public = generated.get_verifying_key().to_string() # not logged for security reasons. # log(f"generated private key '{private.hex()}'") log(f"generated public key '{public.hex()}'") return Address.create_from(private, public)
def process_request(self, request, response): """Get session ID from request cookies and load session.""" if self.cookiename in request.cookies: # try to recover old session self.session = Session(request.cookies[self.cookiename], cookiename=self.cookiename) log(2,"Loaded from Session: %s" % self.session.data) else: # new session self.session = Session(cookiename=self.cookiename) log(2, "Created New Session") request.session = self.session # assignment does not copy objects!
def process_response(self, response): """Add Session cookie to repsonse or delete session cookie if no session.""" if self.session and self.session.sessid: self.session.save() # write session to data store response.add_cookie(self.session.make_cookie()) log(2, "Added Session Cookie") else: # delete nonexistant or destroyed empty sessions response.add_cookie(self.session.make_delete_cookie()) log(2, "Delete Session Cookie")
def send(self, code=None, headers=None, body=""): if not headers: headers = {} def w(txt): """Decode as UTF-8 and write to client connection""" self.conn.write(bytes(txt, 'UTF-8')) # method parameters overwrite/add to instance variables if code: self.code = code if body: self.body = body if headers: self.headers.update(headers) # Set ETag in Header tag = self.generate_eTag(self.body) if tag == self.etag: self.body = None self.code = 304 # default values if not self.code: self.code = 200 if 'Content-Type' not in self.headers: self.headers['Content-Type'] = "text/html; charset=utf-8" from server.statuscodes import statuscode (phrase, explanation) = statuscode(self.code) w("HTTP/1.1 %d %s\n" % (self.code, phrase)) log(1, "HTTP/1.1 %d %s (%s)\n" % (self.code, phrase, explanation)) import datetime w("Date: %s\n" % datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S")) w("Content-Type: %s\n" % self.headers['Content-Type']) w("Connection: close\n") for key, value in self.headers.items(): w("%s: %s\n" % (key, value)) #Etag Header setzen w("ETag: {}\n".format(tag)) # Addition: Set proper Content Length if self.body and not 'Content-Length' in self.headers: w("Content-Length: %d\n" % len(self.body)) w("\n") # extra leerzeile schliesst header ab if self.body: if isinstance(self.body, str): w(self.body) else: self.conn.write(self.body)
def serve(self): """ Listen for http requests forever and trigger processing. """ try: c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) c.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # ignore TIME_WAIT (also has other side effects!) c.bind(('localhost', self.port)) c.listen(1) except socket.error as msg: log(0, msg) return log(0, "Server running on http://{}:{} .".format('localhost', self.port)) while 1: csock, caddr = c.accept() conn = csock.makefile(mode='rwb', buffering=1) self.request = Request() self.response = Response(conn) if self.request.parse(conn): # Blatt 3: Pass etag to response (ugly, we will get to know a more elegant architecture for this later) self.response.etag = self.request.etag self.request.origin = caddr[0] log(2, "Request: %s\n" % self.request) try: # processing (check registered routes for actions) processed = False for route in self.routes: log( 2, "Matche %s gegen %s" % (route[0], self.request.resource)) match = re.match(route[0], self.request.resource) if match: log( 2, "Route %s matcht Request %s" % (route[0], self.request.resource)) route[1](self.request, self.response, match) processed = True break if not processed: raise StopProcessing(404, "No matching route.") except StopProcessing as spe: self.response.send(code=spe.code, body=spe.reason) except ConnectionAbortedError as cae: print("Client closed connection: {}. Ignore and go on.". format(cae)) conn.close() csock.close()
def parse(self, profile): if profile is not None: try: result = self.decode_html(profile['body']['storage']['value']) result['name'] = profile['title'] result['id'] = profile['id'] return result except: log("skipped broken profile {}".format(profile['title'])) return None
def findByUsername(self, username): with self.con: cur = self.con.cursor() cur.execute("SELECT * FROM users WHERE username=?", username) row = cur.fetchone() log(2, "findByUsername(%s)=%s" % (username, row if row else "[empty result]")) if row: return User(row) else: None # None if no user found
def send(self, code=None, headers=None, body=""): if not headers: headers = {} def w(txt): """Decode as UTF-8 and write to client connection""" self.conn.write(bytes(txt, 'UTF-8')) # method parameters overwrite/add to instance variables if code: self.code = code if body: self.body = body if headers: self.headers.update(headers) # default values if not self.code: self.code = 200 if 'Content-Type' not in self.headers: self.headers['Content-Type'] = "text/html; charset=UTF-8" from server.statuscodes import statuscode (phrase, explanation) = statuscode(self.code) w("HTTP/1.1 %d %s\n" % (self.code, phrase)) log(1, "HTTP/1.1 %d %s (%s)\n" % (self.code, phrase, explanation)) import datetime w("Date: %s\n" % datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S")) w("Connection: close\n") for key, value in self.headers.items(): w("%s: %s\n" % (key, value)) # Addition: Set proper Content Length if self.body and 'Content-Length' not in self.headers: if isinstance( self.body, str ): # we need length in bytes, so we might have to convert it first length = len(bytes(self.body, 'UTF8')) else: length = len(self.body) w("Content-Length: %d\n" % length) w("\n") # extra leerzeile schliesst header ab if self.body: if isinstance(self.body, str): w(self.body) else: self.conn.write(self.body)
def __init__(self, host, port, url, command, data, mimeType): log.log(2, "creating command thread for command %s" % command) self._connection = http.HTTPConnection(host, port, timeout=self.commandTimeout) self.url = url self.cmd_name = command self.data = data self.mimeType = mimeType threading.Thread.__init__(self) self.answer = None
def set_number_trigger(self, value): headers = {} headers['Content-type'] = 'application/json; charset=utf-8' data = json.dumps({'value': value}) log.log(3, "setting ntrigger to %s" % value) self._connection.request('PUT', self.ntrigger_url, body=data, headers=headers) response = self._connection.getresponse() log.log(3, "setting ntrigger returns %s " % str(response))
def serve(self): """ Listen for http requests forever and trigger processing. """ try: c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) c.bind(('localhost', self.port)) c.listen(1) except socket.error as msg: log(0, msg) return log(0, "Server running on http://{}:{} .".format('localhost', self.port)) while 1: csock, caddr = c.accept() conn = csock.makefile(mode='rwb', buffering=1) self.request = Request() if self.request.parse(conn): self.request.origin = caddr[0] log(2, "Request: %s\n" % self.request) etag = self.request.get_etag() if etag is False: self.response = Response(conn) else: self.response = Response(conn, etag) try: # processing (check registered routes for actions) processed = False for route in self.routes: log( 2, "Matche %s gegen %s" % (route[0], self.request.resource)) match = re.match(route[0], self.request.resource) if match: log( 2, "Route %s matcht Request %s" % (route[0], self.request.resource)) route[1](self.request, self.response, match) processed = True break if not processed: raise StopProcessing(404, "No matching route.") except StopProcessing as spe: self.response.send(code=spe.code, body=spe.reason) conn.close() csock.close()
async def run(loop, lamp): lifx = lamps.CircadianLifx() league = LeagueApi(loop) lamp = lifx.get_device_by_name(lamp) while True: try: health = await league.health_percent() lifx.set_color(lamp, color(health)) await asyncio.sleep(0.5) except: log("no active game - sleeping for 5 seconds.") await asyncio.sleep(5)
def load_from_file(): log("loading configuration from '{}'..".format(CONFIG_FILE)) with open(CONFIG_FILE, 'r') as file: config = yaml.safe_load(file) log('configuration parsed.') configurations = {} for lamp in config['lamps']: lamp_config = LampConfiguration(lamp['name']) for schema in lamp['schema']: lamp_config.add_schema(SchemaConfiguration(**schema)) configurations[lamp['name']] = lamp_config return configurations
def run_command(self, cmdname, url): log.log(2, "command %s - (%s) is running" % (self.cmd_name, cmdname)) headers = {} headers['Content-type'] = self.mimeType self._connection.request('PUT', url, body=self.data, headers=headers) response = self._connection.getresponse() status = response.status data = response.read() if status == 200: data_l = json.loads(data) if cmdname in ['arm', 'trigger']: if 'sequence_id' in data_l.keys(): answer = data_l['sequence id'] else: log.log( 3, "answer to %s contains keys: %s" % (cmdname, data_l.keys())) answer = data_l else: answer = data_l log.log(2, "response from %s command : %s" % (cmdname, str(answer))) return answer else: log.log( 1, "cannot get response from %s command - status: %s" % (cmdname, status)) return None
def login(self, username, password): """Checks credentials and return user object if they are correct.""" with self.con: cur = self.con.cursor() query = "SELECT * FROM users WHERE username=? AND password=?" cur.execute(query, username, password) row = cur.fetchone() log(2, "login(%s,%s)=%s" % (username, password, row if row else "[empty result]")) if row: return User(row) else: return None
async def all(self): """ lists all profiles and then resolves each. """ log('retrieving profile cards..') start = time.monotonic() profiles = await asyncio.gather(*list( map(lambda profile: self.retrieve(str(profile['id'])), await self.list()))) elapsed = "%0.2fs" % (time.monotonic() - start, ) log("retrieved {} profile cards in {}".format(len(profiles), elapsed)) return { "hits": len(profiles), "updated": time.time() * 1000, "time": elapsed, "applicants": list(filter(None, map(self.parse, profiles))) }
async def update(): con = aiohttp.TCPConnector(limit=8) auth = aiohttp.BasicAuth(username, password) async with aiohttp.ClientSession(connector=con, auth=auth) as session: profiles = ProfileApi(session) while True: cards = await profiles.all() with open(OUTPUT, 'w') as output: logger.log('writing user profiles to {}'.format(OUTPUT)) output.write(json.dumps(cards, indent=4, sort_keys=False)) logger.log( 'user profiles written to file, next update in {}s.'.format( UPDATE)) await asyncio.sleep(UPDATE)
def parse(self, conn): """Parses an http-Request and return a dictionary with process_request line values and headers.""" self.headers = {} # read process_request line request_line = conn.readline().decode('utf-8').strip() log(1, "Request-Line: %s" % request_line) if not request_line: # rfc says "server SHOULD ignore blank request lines" return None # parse process_request line try: self.method, self.resource, self.protocol = request_line.split(" ") except ValueError: raise StopProcessing(400, "Bad request-line: %s\n" % request_line) # parse resource to path and params # extract GET parameters from urllib.parse import urlparse, parse_qs # analyse urls and parse query strings requrl = urlparse(self.resource) self.path = requrl.path self.params.update(parse_qs(requrl.query)) # read and parse Request-Headers while True: header_line = conn.readline().decode('utf-8').strip() if not header_line: break log(2, "Header-Line: " + header_line) (headerfield, headervalue) = header_line.split(":", 1) self.headers[headerfield.strip()] = headervalue.strip() # read cookies if 'Cookie' in self.headers: log(2, "Cookie ist: %s" % self.headers['Cookie']) self.cookies = Cookie.parse(self.headers['Cookie']) else: self.cookies = {} # parse POST parameters log(1,"Methode %s" % self.method) if self.method.lower() == 'post' or self.method.lower() == 'put': postbody = conn.read(int(self.headers['Content-Length'])).decode('utf-8') if 'Content-Type' in self.headers and self.headers['Content-Type'] == 'application/json': # JSON data try: self.jsondata = json.loads(postbody) except json.decoder.JSONDecodeError as err: raise StopProcessing(code=400, reason="Unable to decode JSON data: {}".format(err)) else: # default is x-www-form-urlencoded self.params.update(parse_qs(postbody)) # all parameter values are lists # replace lists by the only element if there is only one for key in self.params: if len(self.params[key])==1: self.params[key] = self.params[key][0] return self.headers
def commit(self): def w(txt): """Decode as UTF-8 and write to client connection""" self.conn.write(bytes(txt, 'UTF-8')) # default values if not self.code: self.code = 200 if 'Content-Type' not in self.headers: self.headers['Content-Type'] = "text/html; charset=UTF-8" from server.statuscodes import statuscode (phrase, explanation) = statuscode(self.code) w("HTTP/1.1 %d %s\n" % (self.code, phrase)) log(1, "HTTP/1.1 %d %s (%s)\n" % (self.code, phrase, explanation)) import datetime w("Date: %s\n" % datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S")) w("Connection: close\n") # send cookies for c in self.cookies: w(c.get_header()) # send other headers for key, value in self.headers.items(): w("%s: %s\n" % (key, value)) # Addition: Set proper Content Length if self.body and 'Content-Length' not in self.headers: if isinstance( self.body, str ): # we need length in bytes, so we might have to convert it first length = len(bytes(self.body, 'UTF8')) else: length = len(self.body) w("Content-Length: %d\n" % length) w("\n") # extra leerzeile schliesst header ab if self.body: if isinstance(self.body, str): # UTF-8 w(self.body) else: # bytes, e.g. binary data self.conn.write(self.body)
def serve(self): log("serving contents of /web.") app = web.Application() app.add_routes([ web.get('/', self.index), web.post('/api/info', self.info), web.post('/api/blockhash', self.blockhash), web.post('/api/block', self.block), web.post('/api/minecraft', self.mine), web.post('/api/txinfo', self.txinfo), web.post('/api/outputs', self.outputs), web.get('/api/address', self.address), web.post('/api/transact', self.transact) ]) app.router.add_static('/', './web') web.run_app(app)
async def call(self, method, params=[], cache=False): data = {"method": method, "params": params} log(f"invoking rpc call '{method}'") payload = json.dumps(data) tag = hash(payload) try: if cache and tag in self.cache: return self.cache[tag] else: async with self.session.post(URL, data=payload) as response: response = json.loads(await response.text()) if cache: self.cache[tag] = response return response except Exception as e: log("Failed to call {}, {}".format(URL, repr(e))) return None
def updateCommandStatus(self): if self.detector_command is None: return cmd_alive = self.detector_command.is_alive() if cmd_alive is False: try: cmd_name = self.detector_command.getCommandName() ret_value = self.detector_command.getCommandAnswer() self.cmd_answer[cmd_name] = ret_value log.log( 2, "command %s finished execution. answer is: %s" % (cmd_name, ret_value)) except BaseException as e: log.log(1, "error update command: %s" % str(e)) self.detector_command = None
def __init__(self, host='127.0.0.1', port=80, verbose=False, urlPrefix=None, user=None): """ Create a client object to talk to the EIGER API. Args: host: hostname of the detector computer port: port usually 80 (http) verbose: bool value urlPrefix: String prepended to the urls. Should be None. Added for future convenience. user: "******". Should be None. Added for future convenience. """ super(DEigerClient, self).__init__() self._host = host self._port = port self._version = Version self._verbose = verbose self._urlPrefix = "" self._user = None self._connectionTimeout = MAX_TIMEOUT #pingret = os.system("ping -c 1 -w 1 -W 10 %s > /dev/null 2>&1" % self._host) #if pingret != 0: #raise BaseException("Cannot access DCU") try: self._connection = http.HTTPConnection( self._host, self._port, timeout=self._connectionTimeout) except BaseException as exc: log.log(1, "Exception while connecting to Eiger DCU %s" % str(exc)) raise (e) self._serializer = None self.detector_command = None self.cmd_answer = {} self.read_thread = None self.read_value = {} self.setUrlPrefix(urlPrefix) self.setUser(user)
def startAcquisition(self, need_arm=True, trigger_mode='ints', ntrigger=1): if self.isExecutingCommand(): log.log(1, "cannot start acquisition while a command is running") return False arm_url = self._url('detector', 'command', 'arm') trigger_url = self._url('detector', 'command', 'trigger') ntrigger_url = self._url('detector', 'config', 'ntrigger') self.detector_command = StartAcquisitionThread(self._host, self._port, \ arm_url, trigger_url, ntrigger_url) self.detector_command.set_arm_needed(need_arm) self.detector_command.set_trigger_mode(trigger_mode) self.detector_command.set_ntrigger(ntrigger) self.startDetectorCommand() return True
def authenticate(self): """Check if user is authenticated. If not, send 401.""" log(2, "Checking for authentication") if self.request.user: # log(2, "Authentication OK") return True else: log(2, "Authentication not OK, send 401") self.response.add_header( "WWW-Authenticate", 'RestBasic realm="We need your password."') self.response.add_header('Content-Type', 'application/json') raise StopProcessing( 401, json.dumps({"error": "Authentiction required."}))
def parse(self, conn): """Parses an http-Request and return a dictionary with process_request line values and headers.""" self.headers = {} # read process_request line request_line = conn.readline().decode('utf-8').strip() log(1, "Request-Line: %s" % request_line) if not request_line: # rfc says "server SHOULD ignore blank request lines" return None # parse process_request line try: self.method, self.resource, self.protocol = request_line.split(" ") except ValueError: raise StopProcessing(400, "Bad request-line: %s\n" % request_line) # parse resource to path and params # extract GET parameters from urllib.parse import urlparse, parse_qs # analyse urls and parse query strings requrl = urlparse(self.resource) self.path = requrl.path self.params.update(parse_qs(requrl.query)) # read and parse Request-Headers while True: header_line = conn.readline().decode('utf-8').strip() if not header_line: break log(2, "Header-Line: " + header_line) (headerfield, headervalue) = header_line.split(":", 1) self.headers[headerfield.strip()] = headervalue.strip() # parse POST parameters log(1, "Methode %s" % self.method) if self.method == 'POST' or self.method == 'post': postbody = conn.read(int( self.headers['Content-Length'])).decode('utf-8') self.params.update(parse_qs(postbody)) # all parameter values are lists # replace lists by the only element if there is only one for key in self.params: if len(self.params[key]) == 1: self.params[key] = self.params[key][0] # Blatt 03: Add Etag handling (If-None-Matches header) if "If-None-Match" in self.headers: self.etag = self.headers['If-None-Match'].strip( '"') # strip off quotes return self.headers
host='localhost' ## Server port. port=8000 ## Display the help of the server. def displayHelp(): print("python server.py [host port]\n\tStart a local server at address ws://%s:%s/." % (host, port)) print("python server.py -d\n\tStart a server at address ws://%s:%s/." % (socket.gethostbyname(socket.gethostname()), port)) if __name__ == '__main__': # Parse command line arguments if len(sys.argv)==1: pass elif len(sys.argv)==2 and sys.argv[1]=="-d": host=socket.gethostbyname(socket.gethostname()) elif len(sys.argv)==3: host=sys.argv[1] port=int(sys.argv[2]) else: displayHelp() exit(1) # Start the computation thread threading.Thread(target=simulationCompute).start() # Start the server log("Server started on ws://%s:%s/ with PID %s." % (host, port, os.getpid())) start_server = websockets.serve(clientHandler, host, port, klass=IPGetterProtocol) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()