def _process_error(self, failure):
     err = failure.value
     if isinstance(err, gcmclient.GCMAuthenticationError):
         self.log.error("GCM Authentication Error: %s" % err)
         raise RouterException("Server error", status_code=500, errno=901)
     if isinstance(err, TimeoutError):
         self.log.warn("GCM Timeout: %s" % err)
         self.metrics.increment("notification.bridge.error",
                                tags=make_tags(self._base_tags,
                                               reason="timeout"))
         raise RouterException("Server error",
                               status_code=502,
                               errno=903,
                               log_exception=False)
     if isinstance(err, ConnectError):
         self.log.warn("GCM Unavailable: %s" % err)
         self.metrics.increment("notification.bridge.error",
                                tags=make_tags(
                                    self._base_tags,
                                    reason="connection_unavailable"))
         raise RouterException("Server error",
                               status_code=502,
                               errno=902,
                               log_exception=False)
     return failure
示例#2
0
 def _process_reply(self, reply, notification, router_data, ttl):
     """Process FCM send reply"""
     # acks:
     #  for reg_id, msg_id in reply.success.items():
     # updates
     result = reply.get('results', [{}])[0]
     if reply.get('canonical_ids'):
         old_id = router_data['token']
         new_id = result.get('registration_id')
         self.log.debug("FCM id changed : {old} => {new}",
                        old=old_id, new=new_id)
         self.metrics.increment("notification.bridge.error",
                                tags=make_tags(self._base_tags,
                                               reason="reregister"))
         return RouterResponse(status_code=503,
                               response_body="Please try request again.",
                               router_data=dict(token=new_id))
     if reply.get('failure'):
         self.metrics.increment("notification.bridge.error",
                                tags=make_tags(self._base_tags,
                                               reason="failure"))
         reason = result.get('error', "Unreported")
         err = self.reasonTable.get(reason)
         if err.get("crit", False):
             self.log.critical(
                 err['msg'],
                 nlen=notification.data_length,
                 regid=router_data["token"],
                 senderid=self.senderID,
                 ttl=notification.ttl,
             )
             raise RouterException("FCM failure to deliver",
                                   status_code=err['err'],
                                   response_body="Please try request "
                                                 "later.",
                                   log_exception=False)
         creds = router_data["creds"]
         self.log.debug("{msg} : {info}",
                        msg=err['msg'],
                        info={"senderid": creds.get('registration_id'),
                              "reason": reason})
         return RouterResponse(
             status_code=err['err'],
             errno=err['errno'],
             response_body=err['msg'],
             router_data={},
         )
     self.metrics.increment("notification.bridge.sent",
                            self._base_tags)
     self.metrics.gauge("notification.message_data",
                        notification.data_length,
                        tags=make_tags(self._base_tags,
                                       destination="Direct"))
     location = "%s/m/%s" % (self.ap_settings.endpoint_url,
                             notification.version)
     return RouterResponse(status_code=201, response_body="",
                           headers={"TTL": ttl,
                                    "Location": location},
                           logged_status=200)
示例#3
0
 def _process_error(self, failure):
     err = failure.value
     if isinstance(err, FCMAuthenticationError):
         self.log.error("FCM Authentication Error: {}".format(err))
         raise RouterException("Server error", status_code=500, errno=901)
     if isinstance(err, TimeoutError):
         self.log.warn("FCM Timeout: %s" % err)
         self.metrics.increment("notification.bridge.error",
                                tags=make_tags(
                                    self._base_tags,
                                    reason="timeout",
                                    error=502,
                                    errno=903,
                                ))
         raise RouterException("Server error",
                               status_code=502,
                               errno=903,
                               log_exception=False)
     if isinstance(err, ConnectError):
         self.log.warn("FCM Unavailable: %s" % err)
         self.metrics.increment("notification.bridge.error",
                                tags=make_tags(
                                    self._base_tags,
                                    reason="connection_unavailable",
                                    error=502,
                                    errno=902,
                                ))
         raise RouterException("Server error",
                               status_code=502,
                               errno=902,
                               log_exception=False)
     if isinstance(err, FCMNotFoundError):
         self.log.debug("FCM Recipient not found: %s" % err)
         self.metrics.increment("notification.bridge.error",
                                tags=make_tags(
                                    self._base_tags,
                                    reason="recipient_gone",
                                    error=404,
                                    errno=106,
                                ))
         raise RouterException("FCM Recipient no longer available",
                               status_code=404,
                               errno=106,
                               log_exception=False)
     if isinstance(err, RouterException):
         self.log.warn("FCM Error: {}".format(err))
         self.metrics.increment("notification.bridge.error",
                                tags=make_tags(self._base_tags,
                                               reason="server_error",
                                               error=502,
                                               errno=0))
     return failure
示例#4
0
    def _route(self, notification, router_data):
        """Blocking FCM call to route the notification"""
        # THIS MUST MATCH THE CHANNELID GENERATED BY THE REGISTRATION SERVICE
        # Currently this value is in hex form.
        data = {"chid": notification.channel_id.hex}
        if not router_data.get("token"):
            raise self._error("No registration token found. "
                              "Rejecting message.",
                              410, errno=106, log_exception=False)
        regid = router_data.get("token")
        # Payload data is optional. The endpoint handler validates that the
        # correct encryption headers are included with the data.
        if notification.data:
            mdata = self.config.get('max_data', 4096)
            if notification.data_length > mdata:
                raise self._error("This message is intended for a " +
                                  "constrained device and is limited " +
                                  "to 3070 bytes. Converted buffer too " +
                                  "long by %d bytes" %
                                  (notification.data_length - mdata),
                                  413, errno=104, log_exception=False)

            data['body'] = notification.data
            data['con'] = notification.headers['encoding']

            if 'encryption' in notification.headers:
                data['enc'] = notification.headers['encryption']
            if 'crypto_key' in notification.headers:
                data['cryptokey'] = notification.headers['crypto_key']
            elif 'encryption_key' in notification.headers:
                data['enckey'] = notification.headers['encryption_key']

        # registration_ids are the FCM instance tokens (specified during
        # registration.
        router_ttl = min(self.MAX_TTL,
                         max(self.min_ttl, notification.ttl or 0))
        try:
            result = self.fcm.notify_single_device(
                collapse_key=self.collapseKey,
                data_message=data,
                dry_run=self.dryRun or ('dryrun' in router_data),
                registration_id=regid,
                time_to_live=router_ttl,
            )
        except pyfcm.errors.AuthenticationError as e:
            self.log.error("Authentication Error: %s" % e)
            raise RouterException("Server error", status_code=500)
        except ConnectionError as e:
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(
                                       self._base_tags,
                                       reason="connection_unavailable"))
            self.log.warn("Could not connect to FCM server: %s" % e)
            raise RouterException("Server error", status_code=502,
                                  log_exception=False)
        except Exception as e:
            self.log.error("Unhandled FCM Error: %s" % e)
            raise RouterException("Server error", status_code=500)
        return self._process_reply(result, notification, router_data,
                                   ttl=router_ttl)
示例#5
0
 def stored_response(self, notification):
     self.metrics.increment("notification.message_data",
                            notification.data_length,
                            tags=make_tags(destination='Direct'))
     location = "%s/m/%s" % (self.conf.endpoint_url, notification.location)
     return RouterResponse(status_code=201, response_body="",
                           headers={"Location": location,
                                    "TTL": notification.ttl},
                           logged_status=202)
示例#6
0
    def _router_completed(self,
                          response,
                          uaid_data,
                          warning="",
                          router_type=None,
                          vapid=None):
        """Called after router has completed successfully"""
        # Log the time taken for routing
        self._timings["route_time"] = time.time() - self._router_time
        # Were we told to update the router data?
        time_diff = time.time() - self._start_time
        if response.router_data is not None:
            if not response.router_data:
                # An empty router_data object indicates that the record should
                # be deleted. There is no longer valid route information for
                # this record.
                self.log.debug(format="Dropping User",
                               code=100,
                               uaid_hash=hasher(uaid_data["uaid"]),
                               uaid_record=dump_uaid(uaid_data),
                               client_info=self._client_info)
                d = deferToThread(self.db.router.drop_user, uaid_data["uaid"])
                d.addCallback(lambda x: self._router_response(response))
                return d
            # The router data needs to be updated to include any changes
            # requested by the bridge system
            uaid_data["router_data"] = response.router_data
            # set the AWS mandatory data
            uaid_data["connected_at"] = ms_time()
            d = deferToThread(self.db.router.register_user, uaid_data)
            response.router_data = None
            d.addCallback(lambda x: self._router_completed(
                response, uaid_data, warning, router_type, vapid))
            return d
        else:
            # No changes are requested by the bridge system, proceed as normal
            dest = 'Direct'
            if response.status_code == 200 or response.logged_status == 200:
                self.log.debug(format="Successful delivery",
                               client_info=self._client_info)
            elif response.status_code == 202 or response.logged_status == 202:
                self.log.debug(format="Router miss, message stored.",
                               client_info=self._client_info)
                dest = 'Stored'
            self.metrics.timing("notification.request_time",
                                duration=time_diff)
            self.metrics.increment('notification.message.success',
                                   tags=make_tags(destination=dest,
                                                  router=router_type,
                                                  vapid=(vapid is not None)))

            response.response_body = (response.response_body + " " +
                                      warning).strip()
            self._router_response(response)
示例#7
0
    def validate_uaid_month_and_chid(self, d):
        db = self.context["db"]  # type: DatabaseManager

        try:
            result = db.router.get_uaid(d["uaid"].hex)
        except ItemNotFound:
            raise InvalidRequest("UAID not found", status_code=410, errno=103)

        # We must have a router_type to validate the user
        router_type = result.get("router_type")
        if router_type not in VALID_ROUTER_TYPES:
            self.context["log"].debug(format="Dropping User",
                                      code=102,
                                      uaid_hash=hasher(result["uaid"]),
                                      uaid_record=repr(result))
            self.context["metrics"].increment("updates.drop_user",
                                              tags=make_tags(errno=102))
            self.context["db"].router.drop_user(result["uaid"])
            raise InvalidRequest("No such subscription",
                                 status_code=410,
                                 errno=106)

        if (router_type == "gcm" and 'senderID' not in result.get(
                'router_data', {}).get("creds", {})):
            # Make sure we note that this record is bad.
            result['critical_failure'] = \
                result.get('critical_failure', "Missing SenderID")
            db.router.register_user(result)

        if (router_type == "fcm"
                and 'app_id' not in result.get('router_data', {})):
            # Make sure we note that this record is bad.
            result['critical_failure'] = \
                result.get('critical_failure', "Missing SenderID")
            db.router.register_user(result)

        if result.get("critical_failure"):
            raise InvalidRequest("Critical Failure: %s" %
                                 result.get("critical_failure"),
                                 status_code=410,
                                 errno=105)
        # Some stored user records are marked as "simplepush".
        # If you encounter one, may need to tweak it a bit to get it as
        # a valid WebPush record.
        if result["router_type"] == "simplepush":
            result["router_type"] = "webpush"

        if result["router_type"] == "webpush":
            self._validate_webpush(d, result)

        # Propagate the looked up user data back out
        d["user_data"] = result
示例#8
0
 def stored_response(self, notification):
     self.metrics.increment("notification.message_data",
                            notification.data_length,
                            tags=make_tags(destination='Stored'))
     location = "%s/m/%s" % (self.conf.endpoint_url, notification.location)
     # RFC https://tools.ietf.org/html/rfc8030#section-5
     # all responses should be 201, unless this is a push reciept request,
     # which requires a 202 and a URL that can be checked later for UA
     # acknowledgement. (We don't support that yet. See autopush-rs#244)
     return RouterResponse(status_code=201, response_body="",
                           headers={"Location": location,
                                    "TTL": notification.ttl},
                           logged_status=201)
示例#9
0
 def delivered_response(self, notification):
     self.metrics.gauge("notification.message_data",
                        notification.data_length,
                        tags=make_tags(destination='Stored'))
     location = "%s/m/%s" % (self.ap_settings.endpoint_url,
                             notification.location)
     return RouterResponse(status_code=201,
                           response_body="",
                           headers={
                               "Location": location,
                               "TTL": notification.ttl or 0
                           },
                           logged_status=200)
示例#10
0
    def _validate_webpush(self, d, result):
        db = self.context["db"]  # type: DatabaseManager
        log = self.context["log"]  # type: Logger
        metrics = self.context["metrics"]  # type: Metrics
        channel_id = normalize_id(d["chid"])
        uaid = result["uaid"]
        if 'current_month' not in result:
            log.debug(format="Dropping User",
                      code=102,
                      uaid_hash=hasher(uaid),
                      uaid_record=repr(result))
            metrics.increment("updates.drop_user", tags=make_tags(errno=102))
            db.router.drop_user(uaid)
            raise InvalidRequest("No such subscription",
                                 status_code=410,
                                 errno=106)

        month_table = result["current_month"]
        if month_table not in db.message_tables:
            log.debug(format="Dropping User",
                      code=103,
                      uaid_hash=hasher(uaid),
                      uaid_record=repr(result))
            metrics.increment("updates.drop_user", tags=make_tags(errno=103))
            db.router.drop_user(uaid)
            raise InvalidRequest("No such subscription",
                                 status_code=410,
                                 errno=106)
        msg = db.message_table(month_table)
        exists, chans = msg.all_channels(uaid=uaid)

        if (not exists or channel_id.lower() not in map(
                lambda x: normalize_id(x), chans)):
            log.debug("Unknown subscription: {channel_id}",
                      channel_id=channel_id)
            raise InvalidRequest("No such subscription",
                                 status_code=410,
                                 errno=106)
示例#11
0
 def _process_reply(self, reply, notification, router_data, ttl):
     """Process FCM send reply"""
     # Failures are returned as non-200 messages (404, 410, etc.)
     self.metrics.increment("notification.bridge.sent",
                            tags=self._base_tags)
     self.metrics.increment("notification.message_data",
                            notification.data_length,
                            tags=make_tags(self._base_tags,
                                           destination="Direct"))
     location = "%s/m/%s" % (self.conf.endpoint_url, notification.version)
     return RouterResponse(status_code=201,
                           response_body="",
                           headers={
                               "TTL": ttl,
                               "Location": location
                           },
                           logged_status=200)
示例#12
0
 def _router_completed(self, response, uaid_data, warning=""):
     """Called after router has completed successfully"""
     dest = 'Direct'
     if response.status_code == 200 or response.logged_status == 200:
         self.log.debug(format="Successful delivery",
                        client_info=self._client_info)
     elif response.status_code == 202 or response.logged_status == 202:
         self.log.debug(format="Router miss, message stored.",
                        client_info=self._client_info)
         dest = 'Stored'
     time_diff = time.time() - self._start_time
     self.metrics.timing("notification.request_time", duration=time_diff)
     self.metrics.increment('notification.message.success',
                            tags=make_tags(destination=dest,
                                           router='simplepush'))
     response.response_body = (response.response_body + " " +
                               warning).strip()
     self._router_response(response)
示例#13
0
    def _process_reply(self, reply, uaid_data, ttl, notification):
        """Process GCM send reply"""
        # acks:
        #  for reg_id, msg_id in reply.success.items():
        # updates
        for old_id, new_id in reply.canonicals.items():
            self.log.debug("GCM id changed : {old} => {new}",
                           old=old_id, new=new_id)
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(self._base_tags,
                                                  reason="reregister"))
            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:
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(self._base_tags,
                                                  reason="unregistered"))
            self.log.debug("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:
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(self._base_tags,
                                                  reason="failure"))
            self.log.debug("GCM failures: {failed()}",
                           failed=lambda: repr(reply.failed.items()))
            raise RouterException("GCM unable to deliver", status_code=410,
                                  response_body="GCM recipient not available.",
                                  log_exception=False,
                                  )

        # retries:
        if reply.retry_after:
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(self._base_tags,
                                                  reason="retry"))
            self.log.warn("GCM retry requested: {failed()}",
                          failed=lambda: repr(reply.failed.items()))
            raise RouterException("GCM failure to deliver, retry",
                                  status_code=503,
                                  headers={"Retry-After": reply.retry_after},
                                  response_body="Please try request "
                                                "in {} seconds.".format(
                                       reply.retry_after
                                  ),
                                  log_exception=False)

        self.metrics.increment("notification.bridge.sent",
                               tags=self._base_tags)
        self.metrics.increment("notification.message_data",
                               notification.data_length,
                               tags=make_tags(self._base_tags,
                                              destination='Direct'))
        location = "%s/m/%s" % (self.conf.endpoint_url, notification.version)
        return RouterResponse(status_code=201, response_body="",
                              headers={"TTL": ttl,
                                       "Location": location},
                              logged_status=200)
示例#14
0
    def _route(self, notification, uaid_data):
        """Blocking GCM call to route the notification"""
        router_data = uaid_data["router_data"]
        # THIS MUST MATCH THE CHANNELID GENERATED BY THE REGISTRATION SERVICE
        # Currently this value is in hex form.
        data = {"chid": notification.channel_id.hex}
        # Payload data is optional. The endpoint handler validates that the
        # correct encryption headers are included with the data.
        if notification.data:
            mdata = self.router_conf.get('max_data', 4096)
            if notification.data_length > mdata:
                raise self._error("This message is intended for a " +
                                  "constrained device and is limited " +
                                  "to 3070 bytes. Converted buffer too " +
                                  "long by %d bytes" %
                                  (notification.data_length - mdata),
                                  413, errno=104, log_exception=False)

            data['body'] = notification.data
            data['con'] = notification.headers['encoding']

            if 'encryption' in notification.headers:
                data['enc'] = notification.headers.get('encryption')
            if 'crypto_key' in notification.headers:
                data['cryptokey'] = notification.headers['crypto_key']
            elif 'encryption_key' in notification.headers:
                data['enckey'] = notification.headers['encryption_key']

        # registration_ids are the GCM instance tokens (specified during
        # registration.
        router_ttl = min(self.MAX_TTL,
                         max(notification.ttl or 0, self.min_ttl))
        payload = gcmclient.JSONMessage(
            registration_ids=[router_data.get("token")],
            collapse_key=self.collapseKey,
            time_to_live=router_ttl,
            dry_run=self.dryRun or ("dryrun" in router_data),
            data=data,
        )
        try:
            gcm = self.gcm[router_data['creds']['senderID']]
            result = gcm.send(payload)
        except RouterException:
            raise  # pragma nocover
        except KeyError:
            self.log.critical("Missing GCM bridge credentials")
            raise RouterException("Server error", status_code=500,
                                  errno=900)
        except gcmclient.GCMAuthenticationError as e:
            self.log.error("GCM Authentication Error: %s" % e)
            raise RouterException("Server error", status_code=500,
                                  errno=901)
        except ConnectionError as e:
            self.log.warn("GCM Unavailable: %s" % e)
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(
                                       self._base_tags,
                                       reason="connection_unavailable"))
            raise RouterException("Server error", status_code=502,
                                  errno=902,
                                  log_exception=False)
        except Exception as e:
            self.log.error("Unhandled exception in GCM Routing: %s" % e)
            raise RouterException("Server error", status_code=500)
        return self._process_reply(result, uaid_data, ttl=router_ttl,
                                   notification=notification)
示例#15
0
 def delivered_response(self, notification):
     self.metrics.gauge("notification.message_data",
                        notification.data_length,
                        tags=make_tags(destination='Direct'))
     return RouterResponse(200, "Delivered")
示例#16
0
    def _route(self, notification, router_data):
        """Blocking APNS call to route the notification

        :param notification: Notification data to send
        :type notification: dict
        :param router_data: Pre-initialized data for this connection
        :type router_data: dict

        """
        router_token = router_data["token"]
        rel_channel = router_data["rel_channel"]
        apns_client = self.apns[rel_channel]
        # chid MUST MATCH THE CHANNELID GENERATED BY THE REGISTRATION SERVICE
        # Currently this value is in hex form.
        payload = {
            "chid": notification.channel_id.hex,
            "ver": notification.version,
        }
        if notification.data:
            payload["con"] = notification.headers.get(
                "content-encoding", notification.headers.get("encoding"))
            if payload["con"] != "aes128gcm":
                if "encryption" in notification.headers:
                    payload["enc"] = notification.headers["encryption"]
                if "crypto_key" in notification.headers:
                    payload["cryptokey"] = notification.headers["crypto_key"]
                elif "encryption_key" in notification.headers:
                    payload["enckey"] = notification.headers["encryption_key"]
            payload["body"] = notification.data
            payload['aps'] = router_data.get(
                'aps', {
                    "mutable-content": 1,
                    "alert": {
                        "loc-key": "SentTab.NoTabArrivingNotification.body",
                        "title-loc-key":
                        "SentTab.NoTabArrivingNotification.title",
                    }
                })
        apns_id = str(uuid.uuid4()).lower()
        # APNs may force close a connection on us without warning.
        # if that happens, retry the message.
        try:
            apns_client.send(router_token=router_token,
                             payload=payload,
                             apns_id=apns_id)
        except Exception as e:
            # We sometimes see strange errors around sending push notifications
            # to APNS. We get reports that after a new deployment things work,
            # but then after a week or so, messages across the APNS bridge
            # start to fail. The connections appear to be working correctly,
            # so we don't think that this is a problem related to how we're
            # connecting.
            if isinstance(e, ConnectionError):
                reason = "connection_error"
            elif isinstance(e, (HTTP20Error, socket.error)):
                reason = "http2_error"
            else:
                reason = "unknown"
            if isinstance(e, RouterException) and e.status_code in [404, 410]:
                raise RouterException(
                    str(e),
                    status_code=e.status_code,
                    errno=106,
                    response_body="User is no longer registered",
                    log_exception=False)
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(self._base_tags,
                                                  application=rel_channel,
                                                  reason=reason))
            raise RouterException(
                str(e),
                status_code=502,
                response_body="APNS returned an error processing request",
            )

        location = "%s/m/%s" % (self.conf.endpoint_url, notification.version)
        self.metrics.increment("notification.bridge.sent",
                               tags=make_tags(self._base_tags,
                                              application=rel_channel))

        self.metrics.increment("updates.client.bridge.apns.{}.sent".format(
            router_data["rel_channel"]),
                               tags=self._base_tags)
        self.metrics.increment("notification.message_data",
                               notification.data_length,
                               tags=make_tags(self._base_tags,
                                              destination='Direct'))
        return RouterResponse(status_code=201,
                              response_body="",
                              headers={
                                  "TTL": notification.ttl,
                                  "Location": location
                              },
                              logged_status=200)
示例#17
0
    def _route(self, notification, router_data):
        """Blocking APNS call to route the notification

        :param notification: Notification data to send
        :type notification: dict
        :param router_data: Pre-initialized data for this connection
        :type router_data: dict

        """
        router_token = router_data["token"]
        rel_channel = router_data["rel_channel"]
        apns_client = self.apns[rel_channel]
        # chid MUST MATCH THE CHANNELID GENERATED BY THE REGISTRATION SERVICE
        # Currently this value is in hex form.
        payload = {
            "chid": notification.channel_id.hex,
            "ver": notification.version,
        }
        if notification.data:
            payload["body"] = notification.data
            payload["con"] = notification.headers["encoding"]
            payload["enc"] = notification.headers["encryption"]

            if "crypto_key" in notification.headers:
                payload["cryptokey"] = notification.headers["crypto_key"]
            elif "encryption_key" in notification.headers:
                payload["enckey"] = notification.headers["encryption_key"]
            payload['aps'] = router_data.get(
                'aps', {
                    "mutable-content": 1,
                    "alert": {
                        "loc-key": "SentTab.NoTabArrivingNotification.body",
                        "title-loc-key":
                        "SentTab.NoTabArrivingNotification.title",
                    }
                })
        apns_id = str(uuid.uuid4()).lower()
        # APNs may force close a connection on us without warning.
        # if that happens, retry the message.
        success = False
        try:
            apns_client.send(router_token=router_token,
                             payload=payload,
                             apns_id=apns_id)
            success = True
        except ConnectionError:
            self.metrics.increment("notification.bridge.connection.error",
                                   tags=make_tags(self._base_tags,
                                                  application=rel_channel,
                                                  reason="connection_error"))
        except (HTTP20Error, socket.error):
            self.metrics.increment("notification.bridge.connection.error",
                                   tags=make_tags(self._base_tags,
                                                  application=rel_channel,
                                                  reason="http2_error"))
        if not success:
            raise RouterException(
                "Server error",
                status_code=502,
                response_body="APNS returned an error processing request",
                log_exception=False,
            )
        location = "%s/m/%s" % (self.conf.endpoint_url, notification.version)
        self.metrics.increment("notification.bridge.sent",
                               tags=make_tags(self._base_tags,
                                              application=rel_channel))

        self.metrics.increment("updates.client.bridge.apns.{}.sent".format(
            router_data["rel_channel"]),
                               tags=self._base_tags)
        self.metrics.increment("notification.message_data",
                               notification.data_length,
                               tags=make_tags(self._base_tags,
                                              destination='Direct'))
        return RouterResponse(status_code=201,
                              response_body="",
                              headers={
                                  "TTL": notification.ttl,
                                  "Location": location
                              },
                              logged_status=200)
示例#18
0
 def stored_response(self, notification):
     self.metrics.gauge("notification.message_data",
                        notification.data_length,
                        tags=make_tags(destination='Stored'))
     return RouterResponse(202, "Notification Stored")
示例#19
0
    def _route(self, notification, uaid_data):
        """Blocking ADM call to route the notification"""
        router_data = uaid_data["router_data"]
        # THIS MUST MATCH THE CHANNELID GENERATED BY THE REGISTRATION SERVICE
        # Currently this value is in hex form.
        data = {"chid": notification.channel_id.hex}
        # Payload data is optional. The endpoint handler validates that the
        # correct encryption headers are included with the data.
        if notification.data:
            data['body'] = notification.data
            data['con'] = notification.headers['encoding']

            if 'encryption' in notification.headers:
                data['enc'] = notification.headers.get('encryption')
            if 'crypto_key' in notification.headers:
                data['cryptokey'] = notification.headers['crypto_key']

        # registration_ids are the ADM instance tokens (specified during
        # registration.
        ttl = min(self.MAX_TTL, max(notification.ttl or 0, self.min_ttl))

        try:
            adm = self.profiles[router_data['creds']['profile']]
            adm.send(reg_id=router_data.get("token"),
                     payload=data,
                     collapseKey=notification.topic,
                     ttl=ttl)
        except RouterException:
            raise  # pragma nocover
        except Timeout as e:
            self.log.warn("ADM Timeout: %s" % e)
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(self._base_tags,
                                                  reason="timeout"))
            raise RouterException("Server error",
                                  status_code=502,
                                  errno=902,
                                  log_exception=False)
        except ConnectionError as e:
            self.log.warn("ADM Unavailable: %s" % e)
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(
                                       self._base_tags,
                                       reason="connection_unavailable"))
            raise RouterException("Server error",
                                  status_code=502,
                                  errno=902,
                                  log_exception=False)
        except ADMAuthError as e:
            self.log.error("ADM unable to authorize: %s" % e)
            self.metrics.increment("notification.bridge.error",
                                   tags=make_tags(self._base_tags,
                                                  reason="auth failure"))
            raise RouterException("Server error",
                                  status_code=500,
                                  errno=902,
                                  log_exception=False)
        except Exception as e:
            self.log.error("Unhandled exception in ADM Routing: %s" % e)
            raise RouterException("Server error", status_code=500)
        location = "%s/m/%s" % (self.conf.endpoint_url, notification.version)
        return RouterResponse(status_code=201,
                              response_body="",
                              headers={
                                  "TTL": ttl,
                                  "Location": location
                              },
                              logged_status=200)