def exec_app(self): # this is not just for atila, # Corequest need request and response was = the_was._get() was.apps = self.apps was.request = self.request was.response = self.request.response was.env = self.args[0] was.env["skitai.was"] = was response = self.request.response try: content = self.apph(*self.args) will_be_push = make_pushables(response, content) except MemoryError: raise except: was.traceback() trigger.wakeup( lambda p=response, d=self.apph.debug and sys.exc_info() or None: (p.error(500, "Internal Server Error", d), p.done())) else: if will_be_push is None: # not responsible or futures return for part in will_be_push: if len(will_be_push) == 1 and type(part) is bytes and len( response) == 0: response.update("Content-Length", len(part)) response.push(part) trigger.wakeup(lambda p=response: (p.done(), ))
def wrapper(*args, **kwargs): was = the_was._get() if not was.wshasevent(): return f(*args, **kwargs) if was.wsinit(): return was.wsconfig(1, timeout, [ varname, ]) elif was.wsopened(): return onopen and onopen() or '' elif was.wsclosed(): return onclose and onclose() or ''
def exec_app(self): was = the_was._get() was.request = self.request was.env = self.args[0] was.websocket = self.args[0]["websocket"] self.args[0]["skitai.was"] = was content = self.apph(*self.args) if content: if type(content) is not tuple: content = (content, ) was.websocket.send(*content) was.request = None was.env = None was.websocket = None
def handle_request (self, request): def donot_response (self, *args, **kargs): def push (thing): raise AssertionError ("Websocket can't use start_response ()") return push origin = request.get_header ("origin") host = request.get_header ("host") protocol = request.get_header ("sec-websocket-protocol", 'unknown') securekey = request.get_header ("sec-websocket-key") if not origin or not host or not securekey: return request.response.error (400) path, params, query, fragment = request.split_uri () has_route = self.apps.has_route (path) if type (has_route) is int: return request.response.error (404) apph = self.apps.get_app (path) app = apph.get_callable() is_atila = hasattr (app, 'ATILA_THE_HUN') if is_atila: # safari does not support Authorization if request.get_header ("authorization") and not app.is_authorized (request, app.authenticate): return request.response.error (401) if not app.is_allowed_origin (request, app.access_control_allow_origin): return request.response.error (403) env = self.build_environ (request, apph) was = the_was._get () was.request = request was.env = env env ["skitai.was"] = was env ["websocket.event"] = skitai.WS_EVT_INIT message_encoding = skitai.WS_MSG_DEFAULT if not is_atila: # not Skitao-Atila apph (env, donot_response) wsconfig = env.get ("websocket.config", ()) if len (wsconfig) == 3: design_spec_, keep_alive, varnames = wsconfig elif len (wsconfig) == 4: design_spec_, keep_alive, varnames, env ["websocket.session"] = wsconfig else: raise AssertionError ("You should config (design_spec, keep_alive, var_names) where env has key 'skitai.websocket.config'") if type (varnames) not in (list, tuple): varnames = (varnames,) else: current_app, method, kargs, options, resp_code = apph.get_callable().get_method (env ["PATH_INFO"], request) if resp_code: return request.response.error (resp_code) request.routed = current_app.get_routed (method) request.routable = options wsfunc = request.routed if is_atila: fspec = app.get_function_spec (wsfunc) or inspect.getfullargspec (wsfunc) else: fspec = inspect.getfullargspec (wsfunc) savedqs = env.get ('QUERY_STRING', '') current_args = {} defaults = 0 if savedqs: current_args = http_util.crack_query (env ['QUERY_STRING']) if fspec.defaults: defaults = len (fspec.defaults) varnames = fspec.args [1:] temporary_args = "&".join ([arg + "=" for arg in varnames [:len (varnames) - defaults] if current_args.get (arg) is None]) if temporary_args: if savedqs: env ['QUERY_STRING'] = savedqs + "&" + temporary_args else: env ['QUERY_STRING'] = temporary_args apph (env, donot_response) wsconfig = env.get ("websocket.config") if not wsconfig: raise AssertionError ("You should config (design_spec, keep_alive, [data_encoding]) where env has key 'was.wsconfig ()'") if not savedqs and "QUERY_STRING" in env: del env ["QUERY_STRING"] else: env ["QUERY_STRING"] = savedqs keep_alive = 60 if len (wsconfig) == 4: design_spec_, keep_alive, message_encoding, env ["websocket.session"] = wsconfig elif len (wsconfig) == 3: design_spec_, keep_alive, message_encoding = wsconfig elif len (wsconfig) == 2: design_spec_, keep_alive = wsconfig elif len (wsconfig) == 1: design_spec_ = wsconfig [0] del env ["websocket.event"] del env ["websocket.config"] assert (design_spec_ & 31) in (skitai.WS_CHANNEL, skitai.WS_GROUPCHAT, skitai.WS_THREADSAFE_DEPRECATED), "design_spec should be one of (WS_CHANNEL, WS_GROUPCHAT, WS_THREADSAFE)" headers = [ ("Sec-WebSocket-Accept", self.calculate_response_key (securekey)), ("Upgrade", "Websocket"), ("Connection", "Upgrade"), ("WebSocket-Protocol", protocol), ("WebSocket-Location", "ws://" + host + path) ] request.response ("101 Web Socket Protocol Handshake", headers = headers) env ["wsgi.noenv"] = False design_spec = design_spec_ & 31 if design_spec_ & skitai.WS_NOTHREAD == skitai.WS_NOTHREAD: env ["wsgi.multithread"] = 0 elif design_spec_ & skitai.WS_SESSION == skitai.WS_SESSION: env ["wsgi.multithread"] = 0 if design_spec in (skitai.WS_CHANNEL, skitai.WS_THREADSAFE_DEPRECATED): varnames = varnames [:1] # Like AJAX, simple request of client, simple response data # the simplest version of stateless HTTP protocol using basic skitai thread pool ws_class = specs.WebSocket1 if (design_spec_ & skitai.WS_THREADSAFE == skitai.WS_THREADSAFE) or design_spec == skitai.WS_THREADSAFE_DEPRECATED: ws_class = specs.WebSocket6 ws = ws_class (self, request, apph, env, varnames, message_encoding) self.channel_config (request, ws, keep_alive) env ["websocket"] = ws if is_atila: env ["websocket.handler"] = (current_app, wsfunc) ws.open () elif design_spec == skitai.WS_GROUPCHAT: varnames = varnames [:2] param_name = varnames [1] gid = http_util.crack_query (query).get (param_name, None) try: assert gid, "%s value can't find" % param_name except: self.wasc.logger.trace ("server", request.uri) return request.response.error (500, why = apph.debug and sys.exc_info () or None) gid = "%s/%s" % (path, gid) if not servers.websocket_servers.has_key (gid): server = servers.websocket_servers.create (gid, self, request, apph, env, message_encoding) if server is None: return request.response.error (503) env ["websocket"] = server if is_atila: env ["websocket.handler"] = (current_app, wsfunc) server = servers.websocket_servers.get (gid) ws = specs.WebSocket5 (self, request, server, env, varnames) self.channel_config (request, ws, keep_alive) server.add_client (ws) request.channel.die_with (ws, "websocket spec.%d" % design_spec)