def process_publish(self, project, params, info=None): """ Publish message into appropriate channel. """ message, error = yield self.prepare_message(project, params, info) if error: raise Return((False, self.INTERNAL_SERVER_ERROR)) if not message: # message was discarded raise Return((False, None)) # publish prepared message result, error = yield self.publish_message(project, message) if error: raise Return((False, error)) for callback in self.post_publish_callbacks: try: yield callback(project["name"], message) except Exception as err: logger.exception(err) raise Return((True, None))
def process_publish(self, project, params, client=None): """ Publish message into appropriate channel. """ message, error = yield self.prepare_message( project, params, client ) if error: raise Return((False, self.INTERNAL_SERVER_ERROR)) if not message: # message was discarded raise Return((False, None)) # publish prepared message result, error = yield self.publish_message( project, message ) if error: raise Return((False, error)) for callback in self.post_publish_callbacks: try: yield callback(message) except Exception as err: logger.exception(err) raise Return((True, None))
def prepare_message(self, project, params, info): """ Prepare message before actual publishing. """ channel = params.get('channel') if not channel: raise Return((None, None)) data = params.get('data', None) message = { 'uid': uuid.uuid4().hex, 'timestamp': int(time.time()), 'info': info, 'channel': channel, 'data': data } for callback in self.pre_publish_callbacks: try: message = yield callback(project["name"], message) except Exception as err: logger.exception(err) else: if message is None: raise Return((None, None)) raise Return((message, None))
def prepare_message(self, project, params, client): """ Prepare message before actual publishing. """ data = params.get('data', None) message = { 'project_id': project['_id'], 'uid': uuid.uuid4().hex, 'timestamp': int(time.time()), 'client': client, 'channel': params.get('channel'), 'data': data } for callback in self.pre_publish_callbacks: try: message = yield callback(message) except Exception as err: logger.exception(err) else: if message is None: raise Return((None, None)) raise Return((message, None))
def process_publish(self, project, params, allowed_namespaces=None, client=None): """ Publish message into appropriate channel. """ if allowed_namespaces is None: project_namespaces, error = yield self.structure.get_project_namespaces(project) if error: raise Return((None, error)) allowed_namespaces = dict((x["name"], x) for x in project_namespaces) message, error = yield self.prepare_message(project, allowed_namespaces, params, client) if error: raise Return((None, error)) if not message: # message was discarded raise Return((True, None)) # publish prepared message result, error = yield self.publish_message(message, allowed_namespaces) if error: raise Return((None, error)) for callback in self.post_publish_callbacks: try: yield callback(message) except Exception as err: logger.exception(err) raise Return((True, None))
def send(self, response): """ Send message directly to client. """ if not self.sock: raise Return((False, None)) try: self.sock.send(response) except Exception as err: logger.exception(err) yield self.close_sock(pause=False) raise Return((False, None)) raise Return((True, None))
def on_connection_ready(self, ready_callback): project = 'CREATE TABLE IF NOT EXISTS projects (id SERIAL, _id varchar(32) UNIQUE, ' \ 'secret_key varchar(32), options text)' namespace = 'CREATE TABLE IF NOT EXISTS namespaces (id SERIAL, ' \ '_id varchar(32) UNIQUE, project_id varchar(32), ' \ 'name varchar(100) NOT NULL, options text, ' \ 'constraint namespaces_unique unique(project_id, name))' try: yield momoko.Op(self._conn.execute, project, ()) yield momoko.Op(self._conn.execute, namespace, ()) except Exception as err: logger.exception(err) ready_callback()
def prepare_message(self, project, allowed_namespaces, params, client): """ Prepare message before actual publishing. """ namespace_name = params.get("namespace") namespace, error = yield self.structure.get_namespace_by_name(project, namespace_name) if error: raise Return((None, error)) if not namespace: raise Return((None, self.NAMESPACE_NOT_FOUND)) namespace_name = namespace["name"] namespace = allowed_namespaces.get(namespace_name, None) if not namespace: raise Return((None, "namespace not found in allowed namespaces")) data = params.get("data", None) message = { "project_id": project["_id"], "namespace": namespace["name"], "uid": uuid.uuid4().hex, "timestamp": int(time.time()), "client": client, "channel": params.get("channel"), "data": data, } for callback in self.pre_publish_callbacks: try: message = yield callback(message) except Exception as err: logger.exception(err) else: if message is None: raise Return((None, None)) raise Return((message, None))
def authorize(self, auth_address, project, channel): """ Send POST request to web application to ask it if current client has a permission to subscribe on channel. """ project_id = self.project_id http_client = AsyncHTTPClient() request = HTTPRequest( auth_address, method="POST", body=urlencode({ 'user': self.user, 'channel': channel }), request_timeout=1 ) max_auth_attempts = project.get('max_auth_attempts') back_off_interval = project.get('back_off_interval') back_off_max_timeout = project.get('back_off_max_timeout') attempts = 0 while attempts < max_auth_attempts: # get current timeout for project current_attempts = self.application.back_off.setdefault(project_id, 0) factor = random.randint(0, 2**current_attempts-1) timeout = factor*back_off_interval if timeout > back_off_max_timeout: timeout = back_off_max_timeout # wait before next authorization request attempt yield sleep(float(timeout)/1000) try: response = yield http_client.fetch(request) except HTTPError as err: if err.code == 403: # access denied for this client raise Return((False, None)) else: # let it fail and try again after some timeout logger.info("{0} status code when fetching auth address {1}".format( err.code, auth_address )) except Exception as err: logger.error("error fetching auth address {0}".format(auth_address)) logger.exception(err) raise Return((False, None)) else: # reset back-off attempts self.application.back_off[project_id] = 0 if response.code == 200: # auth successful self.update_channel_user_info(response.body, channel) raise Return((True, None)) else: # access denied for this client raise Return((False, None)) attempts += 1 self.application.back_off[project_id] += 1 raise Return((False, None))