def _process_reply(self, reply): """Process GCM send reply""" # acks: # for reg_id, msg_id in reply.success.items(): # updates for old_id, new_id in reply.canonical.items(): log.msg("GCM id changed : %s => " % old_id, new_id) return RouterResponse(status_code=503, response_body="Please try request again.", router_data=dict(token=new_id)) # naks: # uninstall: for reg_id in reply.not_registered: log.msg("GCM no longer registered: %s" % reg_id) return RouterResponse( status_code=410, response_body="Endpoint requires client update", router_data={}, ) # for reg_id, err_code in reply.failed.items(): if len(reply.failed.items()) > 0: log.msg("GCM failures: %s" % json.dumps(reply.failed.items())) raise RouterException("GCM failure to deliver", status_code=503, response_body="Please try request later.") # retries: if reply.needs_retry(): raise RouterException("GCM failure to deliver", status_code=503, response_body="Please try request later.") return RouterResponse(status_code=200, response_body="Message Sent")
def _error(self, err, status, **kwargs): """Error handler that raises the RouterException""" self.log.debug(err, **kwargs) return RouterException(err, status_code=status, response_body=err, **kwargs)
def _save_notification(self, uaid, notification): """Saves a notification, returns a deferred. This version of the overridden method saves each individual message to the message table along with relevant request headers if available. """ if notification.ttl == 0: raise RouterException("Finished Routing", status_code=201, log_exception=False, headers={"TTL": str(notification.ttl)}) headers = None if notification.data: headers = self._crypto_headers(notification) return deferToThread( self.ap_settings.message.store_message, uaid=uaid, channel_id=notification.channel_id, data=notification.data, headers=headers, message_id=notification.version, ttl=notification.ttl, timestamp=int(time.time()), )
def register(self, uaid, router_data): """Validate that a token is in the ``router_data``""" if not router_data.get("token"): raise RouterException("No token registered", status_code=500, response_body="No token registered") return router_data
def test_router_fail_err_400_status(self): from autopush.router.interface import RouterException try: raise RouterException("Abort Ok", status_code=400) except: fail = Failure() self.base._router_fail_err(fail) self.status_mock.assert_called_with(400)
def test_router_fail_err(self): from autopush.router.interface import RouterException try: raise RouterException("error") except: fail = Failure() self.base._router_fail_err(fail) self.status_mock.assert_called_with(500)
def _save_notification(self, uaid, notification, month_table): """Saves a notification, returns a deferred. This version of the overridden method saves each individual message to the message table along with relevant request headers if available. """ if notification.ttl is None: # Note that this URL is temporary, as well as this warning as # we will 400 all missing TTL's eventually raise RouterException( "Missing TTL Header", response_body="Missing TTL Header, see: %s" % TTL_URL, status_code=400, errno=111, log_exception=False, ) if notification.ttl == 0: location = "%s/m/%s" % (self.ap_settings.endpoint_url, notification.version) raise RouterException("Finished Routing", status_code=201, log_exception=False, headers={ "TTL": str(notification.ttl), "Location": location }, logged_status=204) headers = None if notification.data: headers = self._crypto_headers(notification) return deferToThread( self.ap_settings.message_tables[month_table].store_message, uaid=uaid, channel_id=notification.channel_id, data=notification.data, headers=headers, message_id=notification.version, ttl=notification.ttl, timestamp=int(time.time()), )
def preflight_check(self, uaid, channel_id): """Verifies this routing call can be done successfully""" # Locate the user agent's message table record = yield deferToThread(self.ap_settings.router.get_uaid, uaid) if 'current_month' not in record: raise RouterException("No such subscription", status_code=404, log_exception=False, errno=106) month_table = record["current_month"] exists, chans = yield deferToThread( self.ap_settings.message_tables[month_table].all_channels, uaid=uaid) if (not exists or channel_id.lower() not in map( lambda x: normalize_id(x), chans)): raise RouterException("No such subscription", status_code=404, log_exception=False, errno=106) returnValue(month_table)
def bad_register(uaid, connect): raise RouterException("stuff", status_code=500, response_body="Registration badness")
def _error(self, err, status, **kwargs): """Error handler that raises the RouterException""" log.err(err, **kwargs) raise RouterException(err, status_code=status, response_body=err)
def _verify_channel(self, result, channel_id): if channel_id not in result: raise RouterException("No such subscription", status_code=404, log_exception=False, errno=106)
def route_notification(self, notification, uaid_data): """Route a notification to an internal node, and store it if the node can't deliver immediately or is no longer a valid node""" # Determine if they're connected at the moment node_id = uaid_data.get("node_id") uaid = uaid_data["uaid"] self.udp = uaid_data.get("udp") router = self.ap_settings.router # Preflight check, hook used by webpush to verify channel id yield self.preflight_check(uaid, notification.channel_id) # Node_id is present, attempt delivery. # - Send Notification to node # - Success: Done, return 200 # - Error (Node busy): Jump to Save notification below # - Error (Client gone, node gone/dead): Clear node entry for user # - Both: Done, return 503 if node_id: try: result = yield self._send_notification(uaid, node_id, notification) except (ConnectError, UserError, ConnectionRefusedError): self.metrics.increment("updates.client.host_gone") dead_cache.put(node_key(node_id), True) yield deferToThread(router.clear_node, uaid_data).addErrback(self._eat_db_err) raise RouterException("Node was invalid", status_code=503, response_body="Retry Request", log_exception=False, errno=202) if result.code == 200: self.metrics.increment("router.broadcast.hit") returnValue(self.delivered_response(notification)) # Save notification, node is not present or busy # - Save notification # - Success (older version): Done, return 202 # - Error (db error): Done, return 503 try: result = yield self._save_notification(uaid, notification) if result is False: self.metrics.increment("router.broadcast.miss") returnValue(self.stored_response(notification)) except ProvisionedThroughputExceededException: raise RouterException("Provisioned throughput error", status_code=503, response_body="Retry Request", errno=201) # - Lookup client # - Success (node found): Notify node of new notification # - Success: Done, return 200 # - Error (no client): Done, return 202 # - Error (no node): Clear node entry # - Both: Done, return 202 # - Success (no node): Done, return 202 # - Error (db error): Done, return 202 # - Error (no client) : Done, return 404 try: uaid_data = yield deferToThread(router.get_uaid, uaid) except ProvisionedThroughputExceededException: self.metrics.increment("router.broadcast.miss") returnValue(self.stored_response(notification)) except ItemNotFound: self.metrics.increment("updates.client.deleted") raise RouterException("User was deleted", status_code=404, response_body="Invalid UAID", errno=105) # Verify there's a node_id in here, if not we're done node_id = uaid_data.get("node_id") if not node_id: self.metrics.increment("router.broadcast.miss") returnValue(self.stored_response(notification)) try: result = yield self._send_notification_check(uaid, node_id) except (ConnectError, UserError, ConnectionRefusedError): self.metrics.increment("updates.client.host_gone") dead_cache.put(node_key(node_id), True) yield deferToThread(router.clear_node, uaid_data).addErrback(self._eat_db_err) self.metrics.increment("router.broadcast.miss") returnValue(self.stored_response(notification)) if result.code == 200: self.metrics.increment("router.broadcast.save_hit") returnValue(self.delivered_response(notification)) else: self.metrics.increment("router.broadcast.miss") retVal = self.stored_response(notification) if self.udp is not None and "server" in self.conf: # Attempt to send off the UDP wake request. try: yield deferToThread( requests.post(self.conf["server"], data=urlencode(self.udp["data"]), cert=self.conf.get("cert"), timeout=self.conf.get( "server_timeout", 3))) except Exception, x: log.err("Could not send UDP wake request:", str(x)) returnValue(retVal)