class WebInterface(object): """ Allows for easily defining web interfaces for the server. Expects the variable "self.host" to be set to a WebHost object before calling "connect()". See implementation of __init__ class for an example initialization. """ def __init__(self, host=None, host_kwargs={'port': 80}): # starts a WebHost on port 80 that if host is None: from guavacado import WebHost self.host = WebHost(**host_kwargs) else: self.host = host # # add lines like the following: # self.connect('/test/:id','GET_ID','GET') # self.connect('/test/:id',self.GET_ID,'GET') # # to call member function GET_ID() with the argument after "/test/" when a GET request # # is received for "/test/<any text here>" def connect(self, resource, action, method, body_included=False): if isinstance(action, str): callback = getattr(self, action) else: callback = action self.connect_callback(resource, callback, method, body_included=body_included) def connect_callback(self, resource, callback, method, body_included=False): """ connects a specified callback (function) in this object to the specified resource (URL) and http method (GET/PUT/POST/DELETE) """ self.dispatcher = self.host.get_dispatcher() if body_included: disp_callback = callback else: def disp_callback(body, *args): return callback(*args) self.dispatcher.connect(resource, disp_callback, method) # log this connection on the host self.host.log_connection(resource, callback, method) def start_service(self): self.host.start_service() def stop_service(self): self.host.stop_service()
class WebInterface(object): """ Allows for easily defining web interfaces for the server. Expects the variable "self.host" to be set to a WebHost object before calling "connect()". See implementation of __init__ class for an example initialization. """ def __init__(self, host=None, host_kwargs={}, host_addr_kwargs={}, web_dispatcher_ident=None, dispatcher_level=None): # pylint: disable=W0102 if web_dispatcher_ident is None: disp_type = 'web' else: disp_type = ('web', web_dispatcher_ident) # starts a WebHost on port 80 that if host is None: from guavacado import WebHost # pylint: disable=C0415 self.host = WebHost(**host_kwargs) host_addr_kwargs_add = host_addr_kwargs if not web_dispatcher_ident is None: host_addr_kwargs_add.update({'disp_type': disp_type}) self.host.add_addr(**host_addr_kwargs_add) else: self.host = host self.resource_dict = {} self.host.get_specialized_dispatcher(disp_type).add_resource_handler( self.identify_request, self.handle_request, 'WebInterface', level=dispatcher_level) # # add lines like the following: # self.connect('/test/:id','GET_ID','GET') # self.connect('/test/:id',self.GET_ID,'GET') # # to call member function GET_ID() with the argument after "/test/" when a GET request # # is received for "/test/<any text here>" def identify_request( self, req_info={ 'url': None, 'method': None, 'headers': None, 'body': None, 'clientsocket': None, 'extra_response_headers': {} }): # pylint: disable=W0102 if req_info['method'] in self.resource_dict: url_no_browseparams = req_info['url'].split('?')[ 0] # remove and ignore anything after a question mark for (url_prefix, url_param_count, params ) in self.get_possible_split_url_params(url_no_browseparams): if url_prefix in self.resource_dict[req_info['method']]: if url_param_count in self.resource_dict[ req_info['method']][url_prefix]: callback = self.resource_dict[ req_info['method']][url_prefix][url_param_count] args = (req_info['body'], ) + params return (callback, args) return None def handle_request(self, callback, args): return callback(*args) def get_possible_split_url_params(self, url): ret = [] split_url = url.split('/') remaining = split_url removed = [] while len(remaining) > 0: prefix = '/'.join(remaining) param_count = len(split_url) - len(remaining) params = removed[:param_count] if (prefix + '/') == url: ret.append((prefix, param_count, params)) param_count = param_count - 1 params = removed[:param_count] params_decoded = [] for par in params: params_decoded.append(url_decode(par)) params_decoded_tup = tuple(params_decoded) ret.append((prefix, param_count, params_decoded_tup)) removed = [remaining[-1]] + removed remaining = remaining[:-1] return ret def split_url_params(self, url): split_url = url.split('/:') prefix = split_url[0] param_count = len(split_url) - 1 return (prefix, param_count) def connect(self, resource, action, method, body_included=False): if isinstance(action, str): callback = getattr(self, action) else: callback = action self.connect_callback(resource, callback, method, body_included=body_included) def connect_callback(self, resource, callback, method, body_included=False): """ connects a specified callback (function) in this object to the specified resource (URL) and http method (GET/PUT/POST/DELETE) """ self.dispatcher = self.host.get_dispatcher() if body_included: disp_callback = callback else: def disp_callback(_body, *args): return callback(*args) if method not in self.resource_dict: self.resource_dict[method] = {} (url_prefix, url_param_count) = self.split_url_params(resource) if url_prefix not in self.resource_dict[method]: self.resource_dict[method][url_prefix] = {} self.resource_dict[method][url_prefix][url_param_count] = disp_callback # log this connection on the host self.host.get_docs().log_connection(resource, callback, method) def start_service(self): self.host.start_service() def stop_service(self): self.host.stop_service()
class WebSocketInterface(object): def __init__(self, host=None, host_kwargs=None, host_addr_kwargs=None, web_dispatcher_ident=None, dispatcher_level=None, recv_timeout=None): if host_kwargs is None: host_kwargs = {} if host_addr_kwargs is None: host_addr_kwargs = {} if web_dispatcher_ident is None: disp_type = 'web' else: disp_type = ('web', web_dispatcher_ident) if host is None: from guavacado import WebHost # pylint: disable=C0415 self.host = WebHost(**host_kwargs) host_addr_kwargs_add = host_addr_kwargs if not web_dispatcher_ident is None: host_addr_kwargs_add.update({'disp_type': disp_type}) self.host.add_addr(**host_addr_kwargs_add) else: self.host = host self.resource_dict = {} self.recv_timeout = recv_timeout self.host.get_specialized_dispatcher(disp_type).add_resource_handler( self.identify_request, self.handle_request, 'WebInterface', level=dispatcher_level, call_after_handled=self.after_http_handled, override_status_code=101) def identify_request( self, req_info={ 'url': None, 'method': None, 'headers': None, 'body': None, 'clientsocket': None, 'web_request_handler': None, 'extra_response_headers': {} }): # pylint: disable=W0102 if req_info['method'] in self.resource_dict: url_no_browseparams = req_info['url'].split('?')[ 0] # remove and ignore anything after a question mark for (url_prefix, url_param_count, params ) in self.get_possible_split_url_params(url_no_browseparams): if url_prefix in self.resource_dict[req_info['method']]: if url_param_count in self.resource_dict[ req_info['method']][url_prefix]: callbacks_dict = self.resource_dict[ req_info['method']][url_prefix][url_param_count] callback = callbacks_dict['http_callback'] args = (req_info['body'], ) + params websocket_callbacks = { 'connected': callbacks_dict['connected'], 'msg_received': callbacks_dict['received'], 'closed': callbacks_dict['closed'], } return (callback, websocket_callbacks, args, req_info['clientsocket'], req_info['web_request_handler'], req_info['headers'], req_info['extra_response_headers']) return None def handle_request(self, callback, _websocket_callbacks, args, _clientsocket, _web_request_handler, headers, extra_response_headers): _version = headers['Sec-WebSocket-Version'] # only valid value is "13" _upgrade_value = headers['Upgrade'] # only valid value is "websocket" extra_response_headers['Upgrade'] = 'websocket' extra_response_headers['Connection'] = 'Upgrade' extra_response_headers[ 'Sec-WebSocket-Accept'] = WebSocketInterface.calc_response_nonce( headers['Sec-WebSocket-Key']) if callback is None: return b'' return callback(*args) def after_http_handled(self, _callback, websocket_callbacks, args, clientsocket, web_request_handler, _headers, _extra_response_headers): def msg_received(msg): if websocket_callbacks['msg_received'] is not None: websocket_callbacks['msg_received'](handler, msg, *args) clientsocket.settimeout(self.recv_timeout) handler = WebSocketHandler(web_request_handler, msg_received) if websocket_callbacks['connected'] is not None: websocket_callbacks['connected'](handler, *args) handler.run() if websocket_callbacks['closed'] is not None: websocket_callbacks['closed'](handler, *args) def get_possible_split_url_params(self, url): ret = [] split_url = url.split('/') remaining = split_url removed = [] while len(remaining) > 0: prefix = '/'.join(remaining) param_count = len(split_url) - len(remaining) params = removed[:param_count] if (prefix + '/') == url: ret.append((prefix, param_count, params)) param_count = param_count - 1 params = removed[:param_count] params_decoded = [] for par in params: params_decoded.append(url_decode(par)) params_decoded_tup = tuple(params_decoded) ret.append((prefix, param_count, params_decoded_tup)) removed = [remaining[-1]] + removed remaining = remaining[:-1] return ret def split_url_params(self, url): split_url = url.split('/:') prefix = split_url[0] param_count = len(split_url) - 1 return (prefix, param_count) def connect(self, resource, connected=None, received=None, closed=None, http_action=None, method='GET', body_included=False): def getattr_if_str(action): if isinstance(action, str): return getattr(self, action) else: return action connected_callback = getattr_if_str(connected) received_callback = getattr_if_str(received) closed_callback = getattr_if_str(closed) http_callback = getattr_if_str(http_action) self.connect_callback(resource, connected_callback, received_callback, closed_callback, http_callback, method, body_included=body_included) def connect_callback(self, resource, connected=None, received=None, closed=None, http_callback=None, method='GET', body_included=False): """ connects a specified callback (function) in this object to the specified resource (URL) and http method (GET/PUT/POST/DELETE) """ def gen_callback_no_body(cb, body_pos=0): def sub_callback(*args): return cb(*(args[:body_pos] + args[body_pos + 1:])) if cb is not None: return sub_callback else: return None def gen_callback_no_body_if_inluded(cb, body_pos=0): if body_included: return cb else: return gen_callback_no_body(cb, body_pos=body_pos) self.dispatcher = self.host.get_dispatcher() if method not in self.resource_dict: self.resource_dict[method] = {} (url_prefix, url_param_count) = self.split_url_params(resource) if url_prefix not in self.resource_dict[method]: self.resource_dict[method][url_prefix] = {} self.resource_dict[method][url_prefix][url_param_count] = { 'http_callback': gen_callback_no_body_if_inluded(http_callback), 'connected': gen_callback_no_body_if_inluded(connected, body_pos=1), 'received': gen_callback_no_body_if_inluded(received, body_pos=2), 'closed': gen_callback_no_body_if_inluded(closed, body_pos=1), } # log this connection on the host self.host.get_docs().log_connection(resource, http_callback, method, websock_actions={ 'connected': connected, 'received': received, 'closed': closed, }) def start_service(self): self.host.start_service() def stop_service(self): self.host.stop_service() @staticmethod def calc_response_nonce(client_nonce): nonce_hash = hashlib.sha1() nonce_hash.update(client_nonce.encode('utf-8')) nonce_hash.update(b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11') return base64.b64encode(nonce_hash.digest()).decode('utf-8')