Exemplo n.º 1
0
    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")
Exemplo n.º 2
0
 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)
Exemplo n.º 3
0
    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()),
        )
Exemplo n.º 4
0
 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
Exemplo n.º 5
0
    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)
Exemplo n.º 6
0
    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)
Exemplo n.º 7
0
    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()),
        )
Exemplo n.º 8
0
    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)
Exemplo n.º 9
0
 def bad_register(uaid, connect):
     raise RouterException("stuff", status_code=500,
                           response_body="Registration badness")
Exemplo n.º 10
0
 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)
Exemplo n.º 11
0
 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)
Exemplo n.º 12
0
    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)