Beispiel #1
0
 def setUp(self, fgcm):
     settings = AutopushSettings(
         hostname="localhost",
         statsd_host=None,
     )
     self.gcm_config = {'s3_bucket': 'None',
                        'max_data': 32,
                        'ttl': 60,
                        'senderid_list': {'test123':
                                          {"auth": "12345678abcdefg"}}}
     self.gcm = fgcm
     self.router = GCMRouter(settings, self.gcm_config)
     self.headers = {"content-encoding": "aesgcm",
                     "encryption": "test",
                     "encryption-key": "test"}
     # Payloads are Base64-encoded.
     self.notif = Notification(10, "q60d6g", dummy_chid, self.headers,
                               200)
     self.router_data = dict(
         router_data=dict(
             token="connect_data",
             creds=dict(senderID="test123", auth="12345678abcdefg")))
     mock_result = Mock(spec=gcmclient.gcm.Result)
     mock_result.canonical = dict()
     mock_result.failed = dict()
     mock_result.not_registered = dict()
     mock_result.needs_retry.return_value = False
     self.mock_result = mock_result
     fgcm.send.return_value = mock_result
Beispiel #2
0
 def setUp(self):
     conf = AutopushConfig(
         hostname="localhost",
         statsd_host=None,
     )
     self.gcm_config = {
         'max_data': 32,
         'ttl': 60,
         'senderIDs': {
             'test123': {
                 "auth": "12345678abcdefg"
             }
         }
     }
     self.response = Mock(spec=requests.Response)
     self.response.status_code = 200
     self.response.headers = dict()
     self.response.content = json.dumps({
         "multicast_id":
         5174939174563864884,
         "success":
         1,
         "failure":
         0,
         "canonical_ids":
         0,
         "results": [{
             "message_id": "0:1510011451922224%7a0e7efbaab8b7cc"
         }]
     })
     self.gcm = gcmclient.GCM(api_key="SomeKey")
     self.gcm._sender = Mock(return_value=self.response)
     self.router = GCMRouter(conf, self.gcm_config, SinkMetrics())
     self.router.gcm['test123'] = self.gcm
     self.headers = {
         "content-encoding": "aesgcm",
         "encryption": "test",
         "encryption-key": "test"
     }
     # Payloads are Base64-encoded.
     self.notif = WebPushNotification(
         uaid=uuid.UUID(dummy_uaid),
         channel_id=uuid.UUID(dummy_chid),
         data="q60d6g",
         headers=self.headers,
         ttl=200,
         message_id=10,
     )
     self.notif.cleanup_headers()
     self.router_data = dict(router_data=dict(
         token="connect_data",
         creds=dict(senderID="test123", auth="12345678abcdefg")))
Beispiel #3
0
 def test_init(self):
     conf = AutopushConfig(
         hostname="localhost",
         statsd_host=None,
     )
     with pytest.raises(IOError):
         GCMRouter(conf, {"senderIDs": {}}, SinkMetrics())
Beispiel #4
0
 def test_init(self):
     settings = AutopushSettings(
         hostname="localhost",
         statsd_host=None,
     )
     with assert_raises(IOError):
         GCMRouter(settings, {"senderIDs": {}})
Beispiel #5
0
 def setUp(self, fgcm):
     settings = AutopushSettings(
         hostname="localhost",
         statsd_host=None,
     )
     self.gcm_config = {'s3_bucket': 'None',
                        'max_data': 32,
                        'senderid_list': {'test123':
                                          {"auth": "12345678abcdefg"}}}
     self.gcm = fgcm
     self.router = GCMRouter(settings, self.gcm_config)
     self.headers = {"content-encoding": "aesgcm",
                     "encryption": "test",
                     "encryption-key": "test"}
     # Data will most likely be binary values.
     self.notif = Notification(10, "\xab\xad\x1d\xea", dummy_chid,
                               self.headers, 200)
     self.router_data = dict(
         router_data=dict(
             token="connect_data",
             creds=dict(senderID="test123", auth="12345678abcdefg")))
     mock_result = Mock(spec=gcmclient.gcm.Result)
     mock_result.canonical = dict()
     mock_result.failed = dict()
     mock_result.not_registered = dict()
     mock_result.needs_retry.return_value = False
     self.mock_result = mock_result
     fgcm.send.return_value = mock_result
Beispiel #6
0
 def test_gcmclient_fail(self, fgcm):
     fgcm.side_effect = Exception
     settings = AutopushSettings(
         hostname="localhost",
         statsd_host=None,
     )
     with assert_raises(IOError):
         GCMRouter(settings, {"senderIDs": {"test123": {"auth": "abcd"}}})
Beispiel #7
0
 def setUp(self, fgcm):
     settings = AutopushSettings(
         hostname="localhost",
         statsd_host=None,
     )
     self.gcm_config = {
         'max_data': 32,
         'ttl': 60,
         'senderIDs': {
             'test123': {
                 "auth": "12345678abcdefg"
             }
         }
     }
     self.gcm = fgcm
     self.router = GCMRouter(settings, self.gcm_config)
     self.headers = {
         "content-encoding": "aesgcm",
         "encryption": "test",
         "encryption-key": "test"
     }
     # Payloads are Base64-encoded.
     self.notif = WebPushNotification(
         uaid=uuid.UUID(dummy_uaid),
         channel_id=uuid.UUID(dummy_chid),
         data="q60d6g",
         headers=self.headers,
         ttl=200,
         message_id=10,
     )
     self.notif.cleanup_headers()
     self.router_data = dict(router_data=dict(
         token="connect_data",
         creds=dict(senderID="test123", auth="12345678abcdefg")))
     mock_result = Mock(spec=gcmclient.gcm.Result)
     mock_result.canonical = dict()
     mock_result.failed = dict()
     mock_result.not_registered = dict()
     mock_result.needs_retry.return_value = False
     self.mock_result = mock_result
     fgcm.send.return_value = mock_result
Beispiel #8
0
 def setUp(self, fgcm):
     settings = AutopushSettings(
         hostname="localhost",
         statsd_host=None,
     )
     gcm_config = {'s3_bucket': 'None',
                   'senderid_list': {'test123':
                                     {"auth": "12345678abcdefg"}}}
     self.gcm = fgcm
     self.router = GCMRouter(settings, gcm_config)
     self.notif = Notification(10, "data", dummy_chid, None, 200)
     self.router_data = dict(
         router_data=dict(
             token="connect_data",
             creds=dict(senderID="test123", auth="12345678abcdefg")))
     mock_result = Mock(spec=gcmclient.gcm.Result)
     mock_result.canonical = dict()
     mock_result.failed = dict()
     mock_result.not_registered = dict()
     mock_result.needs_retry.return_value = False
     self.mock_result = mock_result
     fgcm.send.return_value = mock_result
Beispiel #9
0
    def setUp(self):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        # Mock out GCM client
        self._old_gcm = gcmclient.GCM
        gcmclient.GCM = Mock(spec=gcmclient.GCM)

        gcm_config = {'apikey': '12345678abcdefg'}
        self.router = GCMRouter(settings, gcm_config)
        self.notif = Notification(10, "data", dummy_chid)
        self.router_data = dict(router_data=dict(token="connect_data"))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        self.router.gcm.send.return_value = mock_result
Beispiel #10
0
class GCMRouterTestCase(unittest.TestCase):
    def setUp(self):
        conf = AutopushConfig(
            hostname="localhost",
            statsd_host=None,
        )
        self.gcm_config = {
            'max_data': 32,
            'ttl': 60,
            'senderIDs': {
                'test123': {
                    "auth": "12345678abcdefg"
                }
            }
        }
        self.response = Mock(spec=requests.Response)
        self.response.status_code = 200
        self.response.headers = dict()
        self.response.content = json.dumps({
            "multicast_id":
            5174939174563864884,
            "success":
            1,
            "failure":
            0,
            "canonical_ids":
            0,
            "results": [{
                "message_id": "0:1510011451922224%7a0e7efbaab8b7cc"
            }]
        })
        self.gcm = gcmclient.GCM(api_key="SomeKey")
        self.gcm._sender = Mock(return_value=self.response)
        self.router = GCMRouter(conf, self.gcm_config, SinkMetrics())
        self.router.gcm['test123'] = self.gcm
        self.headers = {
            "content-encoding": "aesgcm",
            "encryption": "test",
            "encryption-key": "test"
        }
        # Payloads are Base64-encoded.
        self.notif = WebPushNotification(
            uaid=uuid.UUID(dummy_uaid),
            channel_id=uuid.UUID(dummy_chid),
            data="q60d6g",
            headers=self.headers,
            ttl=200,
            message_id=10,
        )
        self.notif.cleanup_headers()
        self.router_data = dict(router_data=dict(
            token="connect_data",
            creds=dict(senderID="test123", auth="12345678abcdefg")))

    def _check_error_call(self, exc, code, response=None, errno=None):
        assert isinstance(exc, RouterException)
        assert exc.status_code == code
        if errno is not None:
            assert exc.errno == errno
        assert self.gcm._sender.called
        if response:
            assert exc.response_body == response
        self.flushLoggedErrors()

    def test_init(self):
        conf = AutopushConfig(
            hostname="localhost",
            statsd_host=None,
        )
        with pytest.raises(IOError):
            GCMRouter(conf, {"senderIDs": {}}, SinkMetrics())

    def test_register(self):
        router_data = {"token": "test123"}
        self.router.register("uaid", router_data=router_data, app_id="test123")
        # Check the information that will be recorded for this user
        assert router_data == {
            "token": "test123",
            "creds": {
                "senderID": "test123",
                "auth": "12345678abcdefg"
            }
        }

    def test_register_bad(self):
        with pytest.raises(RouterException):
            self.router.register("uaid", router_data={}, app_id="")
        with pytest.raises(RouterException):
            self.router.register("uaid", router_data={}, app_id=None)
        with pytest.raises(RouterException):
            self.router.register("uaid",
                                 router_data={"token": "abcd1234"},
                                 app_id="invalid123")

    def test_route_notification(self):
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert self.gcm._sender.called
            # Make sure the data was encoded as base64
            payload = json.loads(self.gcm._sender.call_args[1]['data'])
            data = payload['data']
            assert data['body'] == 'q60d6g'
            assert data['enc'] == 'test'
            assert data['chid'] == dummy_chid
            assert data['enckey'] == 'test'
            assert data['con'] == 'aesgcm'

        d.addCallback(check_results)
        return d

    def test_ttl_none(self):
        self.router.gcm['test123'] = self.gcm
        self.notif = WebPushNotification(uaid=uuid.UUID(dummy_uaid),
                                         channel_id=uuid.UUID(dummy_chid),
                                         data="q60d6g",
                                         headers=self.headers,
                                         ttl=None)
        self.notif.cleanup_headers()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert result.status_code == 201
            assert result.logged_status == 200
            assert "TTL" in result.headers
            assert self.gcm._sender.called
            # Make sure the data was encoded as base64
            payload = json.loads(self.gcm._sender.call_args[1]['data'])
            data = payload['data']
            assert data['body'] == 'q60d6g'
            assert data['enc'] == 'test'
            assert data['chid'] == dummy_chid
            assert data['enckey'] == 'test'
            assert data['con'] == 'aesgcm'
            # use the defined min TTL
            assert payload['time_to_live'] == 60

        d.addCallback(check_results)
        return d

    def test_ttl_high(self):
        self.router.gcm['test123'] = self.gcm
        self.notif = WebPushNotification(uaid=uuid.UUID(dummy_uaid),
                                         channel_id=uuid.UUID(dummy_chid),
                                         data="q60d6g",
                                         headers=self.headers,
                                         ttl=5184000)
        self.notif.cleanup_headers()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert self.gcm._sender.called
            # Make sure the data was encoded as base64
            payload = json.loads(self.gcm._sender.call_args[1]['data'])
            data = payload['data']
            assert data['body'] == 'q60d6g'
            assert data['enc'] == 'test'
            assert data['chid'] == dummy_chid
            assert data['enckey'] == 'test'
            assert data['con'] == 'aesgcm'
            # use the defined min TTL
            assert payload['time_to_live'] == 2419200

        d.addCallback(check_results)
        return d

    def test_long_data(self):
        self.router.gcm['test123'] = self.gcm
        bad_notif = WebPushNotification(
            uaid=uuid.UUID(dummy_uaid),
            channel_id=uuid.UUID(dummy_chid),
            data="\x01abcdefghijklmnopqrstuvwxyz0123456789",
            headers=self.headers,
            ttl=200)

        d = self.router.route_notification(bad_notif, self.router_data)

        def check_results(result):
            assert isinstance(result.value, RouterException)
            assert result.value.status_code == 413
            assert result.value.errno == 104

        d.addBoth(check_results)
        return d

    def test_route_crypto_notification(self):
        del (self.notif.headers['encryption_key'])
        self.notif.headers['crypto_key'] = 'crypto'
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert self.gcm._sender.called

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        self.response.status_code = 401
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500, "Server error", 901)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        self.gcm._sender.side_effect = Exception
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500, "Server error")

        d.addBoth(check_results)
        return d

    def test_router_notification_connection_error(self):
        from requests.exceptions import ConnectionError

        def throw_other(*args, **kwargs):
            raise ConnectionError("oh my!")

        self.gcm._sender.side_effect = throw_other
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 502, "Server error", 902)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.response.content = json.dumps({
            "multicast_id":
            5174939174563864884,
            "success":
            1,
            "failure":
            0,
            "canonical_ids":
            1,
            "results": [{
                "message_id": "0:1510011451922224%7a0e7efbaab8b7cc",
                "registration_id": "new",
            }]
        })
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert result.router_data == dict(token="new")
            assert self.router.metrics.increment.call_args[0][0] == (
                'notification.bridge.error')
            self.router.metrics.increment.call_args[1]['tags'].sort()
            assert self.router.metrics.increment.call_args[1]['tags'] == [
                'platform:gcm', 'reason:reregister'
            ]
            assert self.gcm._sender.called

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.response.content = json.dumps({
            "multicast_id":
            5174939174563864884,
            "success":
            1,
            "failure":
            1,
            "canonical_ids":
            0,
            "results": [{
                "error": "NotRegistered"
            }]
        })
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert result.router_data == dict()
            assert self.router.metrics.increment.call_args[0][0] == (
                'notification.bridge.error')
            self.router.metrics.increment.call_args[1]['tags'].sort()
            assert self.router.metrics.increment.call_args[1]['tags'] == [
                'platform:gcm', 'reason:unregistered'
            ]
            assert self.gcm._sender.called

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.response.content = json.dumps({
            "multicast_id":
            5174939174563864884,
            "success":
            1,
            "failure":
            1,
            "canonical_ids":
            0,
            "results": [{
                "error": "InvalidRegistration"
            }]
        })
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            assert self.router.metrics.increment.called
            assert self.router.metrics.increment.call_args[0][0] == (
                'notification.bridge.error')
            self.router.metrics.increment.call_args[1]['tags'].sort()
            assert self.router.metrics.increment.call_args[1]['tags'] == [
                'platform:gcm', 'reason:failure'
            ]
            assert fail.value.message == 'GCM unable to deliver'
            self._check_error_call(fail.value, 410)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.response.headers['Retry-After'] = "123"
        self.response.status_code = 500
        self.response.content = ""
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            assert self.router.metrics.increment.called
            assert self.router.metrics.increment.call_args[0][0] == (
                'notification.bridge.error')
            self.router.metrics.increment.call_args[1]['tags'].sort()
            assert self.router.metrics.increment.call_args[1]['tags'] == [
                'platform:gcm', 'reason:retry'
            ]
            assert fail.value.message == 'GCM failure to deliver, retry'
            self._check_error_call(fail.value, 503)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {
                                               "token": "abc"
                                           }})

        def check_results(fail):
            assert isinstance(fail.value, RouterException)
            assert fail.value.message == "Server error"
            assert fail.value.status_code == 500
            assert fail.value.errno == 900

        d.addBoth(check_results)
        return d

    def test_amend(self):
        router_data = {"token": "test123"}
        self.router.register("uaid", router_data=router_data, app_id="test123")
        resp = {"key": "value"}
        self.router.amend_endpoint_response(
            resp, self.router_data.get('router_data'))
        assert {"key": "value", "senderid": "test123"} == resp

    def test_register_invalid_token(self):
        with pytest.raises(RouterException):
            self.router.register(uaid="uaid",
                                 router_data={"token": "invalid"},
                                 app_id="invalid")
Beispiel #11
0
class GCMRouterTestCase(unittest.TestCase):

    @patch("gcmclient.gcm.GCM", spec=gcmclient.gcm.GCM)
    def setUp(self, fgcm):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        gcm_config = {'s3_bucket': 'None',
                      'senderid_list': {'test123':
                                        {"auth": "12345678abcdefg"}}}
        self.gcm = fgcm
        self.router = GCMRouter(settings, gcm_config)
        self.notif = Notification(10, "data", dummy_chid, None, 200)
        self.router_data = dict(
            router_data=dict(
                token="connect_data",
                creds=dict(senderID="test123", auth="12345678abcdefg")))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        fgcm.send.return_value = mock_result

    def tearDown(self):
        self.router.senderIDs.stop()

    def _check_error_call(self, exc, code):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        assert(self.router.gcm.send.called)
        self.flushLoggedErrors()

    def test_init(self):
        self.router.senderIDs.get_ID = Mock()

        def throw_ex():
            raise AttributeError
        fsenderids = Mock()
        fsenderids.choose_ID.side_effect = throw_ex
        self.assertRaises(IOError, GCMRouter, {}, {"senderIDs": fsenderids})

    def test_register(self):
        result = self.router.register("uaid", {"token": "connect_data"})
        # Check the information that will be recorded for this user
        eq_(result, {"token": "connect_data",
                     "creds": {"senderID": "test123",
                               "auth": "12345678abcdefg"}})

    def test_register_bad(self):
        self.assertRaises(RouterException, self.router.register, "uaid", {})

    def test_route_notification(self):
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()
        self.gcm.send.side_effect = throw_auth
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")
        self.gcm.send.side_effect = throw_other
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {"token": "abc"}})

        def check_results(fail):
            eq_(fail.value.status_code, 500)
        d.addBoth(check_results)
        return d

    def test_ammend(self):
        self.router.register("uaid", {"token": "connect_data"})
        resp = {"key": "value"}
        eq_({"key": "value", "senderid": "test123"},
            self.router.amend_msg(resp))
Beispiel #12
0
    def __init__(
        self,
        crypto_key=None,
        datadog_api_key=None,
        datadog_app_key=None,
        datadog_flush_interval=None,
        hostname=None,
        port=None,
        router_scheme=None,
        router_hostname=None,
        router_port=None,
        endpoint_scheme=None,
        endpoint_hostname=None,
        endpoint_port=None,
        router_conf={},
        router_tablename="router",
        router_read_throughput=5,
        router_write_throughput=5,
        storage_tablename="storage",
        storage_read_throughput=5,
        storage_write_throughput=5,
        message_tablename="message",
        message_read_throughput=5,
        message_write_throughput=5,
        statsd_host="localhost",
        statsd_port=8125,
        resolve_hostname=False,
        max_data=4096,
        # Reflected up from UDP Router
        wake_timeout=0,
        env='development',
        enable_cors=False,
        s3_bucket=DEFAULT_BUCKET,
        senderid_expry=SENDERID_EXPRY,
        senderid_list={},
        hello_timeout=0,
    ):
        """Initialize the Settings object

        Upon creation, the HTTP agent will initialize, all configured routers
        will be setup and started, logging will be started, and the database
        will have a preflight check done.

        """
        # Use a persistent connection pool for HTTP requests.
        pool = HTTPConnectionPool(reactor)
        self.agent = Agent(reactor, connectTimeout=5, pool=pool)

        # Metrics setup
        if datadog_api_key:
            self.metrics = DatadogMetrics(
                api_key=datadog_api_key,
                app_key=datadog_app_key,
                flush_interval=datadog_flush_interval)
        elif statsd_host:
            self.metrics = TwistedMetrics(statsd_host, statsd_port)
        else:
            self.metrics = SinkMetrics()
        if not crypto_key:
            crypto_key = [Fernet.generate_key()]
        if not isinstance(crypto_key, list):
            crypto_key = [crypto_key]
        self.update(crypto_key=crypto_key)
        self.crypto_key = crypto_key

        self.max_data = max_data
        self.clients = {}

        # Setup hosts/ports/urls
        default_hostname = socket.gethostname()
        self.hostname = hostname or default_hostname
        if resolve_hostname:
            self.hostname = resolve_ip(self.hostname)

        self.port = port
        self.endpoint_hostname = endpoint_hostname or self.hostname
        self.router_hostname = router_hostname or self.hostname

        self.router_conf = router_conf
        self.router_url = canonical_url(router_scheme or 'http',
                                        self.router_hostname, router_port)

        self.endpoint_url = canonical_url(endpoint_scheme or 'http',
                                          self.endpoint_hostname,
                                          endpoint_port)

        # Database objects
        self.router_table = get_router_table(router_tablename,
                                             router_read_throughput,
                                             router_write_throughput)
        self.storage_table = get_storage_table(storage_tablename,
                                               storage_read_throughput,
                                               storage_write_throughput)
        self.message_table = get_message_table(message_tablename,
                                               message_read_throughput,
                                               message_write_throughput)
        self.storage = Storage(self.storage_table, self.metrics)
        self.router = Router(self.router_table, self.metrics)
        self.message = Message(self.message_table, self.metrics)

        # Run preflight check
        preflight_check(self.storage, self.router)

        # CORS
        self.cors = enable_cors

        # Force timeout in idle seconds
        self.wake_timeout = wake_timeout

        # Setup the routers
        self.routers = {}
        self.routers["simplepush"] = SimpleRouter(
            self, router_conf.get("simplepush"))
        self.routers["webpush"] = WebPushRouter(self, None)
        if 'apns' in router_conf:
            self.routers["apns"] = APNSRouter(self, router_conf["apns"])
        if 'gcm' in router_conf:
            self.routers["gcm"] = GCMRouter(self, router_conf["gcm"])

        # Env
        self.env = env

        self.hello_timeout = hello_timeout
Beispiel #13
0
class GCMRouterTestCase(unittest.TestCase):
    @patch("gcmclient.gcm.GCM", spec=gcmclient.gcm.GCM)
    def setUp(self, fgcm):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        self.gcm_config = {
            'max_data': 32,
            'ttl': 60,
            'senderIDs': {
                'test123': {
                    "auth": "12345678abcdefg"
                }
            }
        }
        self.gcm = fgcm
        self.router = GCMRouter(settings, self.gcm_config)
        self.headers = {
            "content-encoding": "aesgcm",
            "encryption": "test",
            "encryption-key": "test"
        }
        # Payloads are Base64-encoded.
        self.notif = WebPushNotification(
            uaid=uuid.UUID(dummy_uaid),
            channel_id=uuid.UUID(dummy_chid),
            data="q60d6g",
            headers=self.headers,
            ttl=200,
            message_id=10,
        )
        self.notif.cleanup_headers()
        self.router_data = dict(router_data=dict(
            token="connect_data",
            creds=dict(senderID="test123", auth="12345678abcdefg")))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        fgcm.send.return_value = mock_result

    def _check_error_call(self, exc, code, response=None):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        ok_(self.router.gcm['test123'].send.called)
        if response:
            eq_(exc.response_body, response)
        self.flushLoggedErrors()

    def test_init(self):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        with assert_raises(IOError):
            GCMRouter(settings, {"senderIDs": {}})

    def test_register(self):
        router_data = {"token": "test123"}
        self.router.register("uaid", router_data=router_data, app_id="test123")
        # Check the information that will be recorded for this user
        eq_(
            router_data, {
                "token": "test123",
                "creds": {
                    "senderID": "test123",
                    "auth": "12345678abcdefg"
                }
            })

    def test_register_bad(self):
        with assert_raises(RouterException):
            self.router.register("uaid", router_data={}, app_id="")
        with assert_raises(RouterException):
            self.router.register("uaid", router_data={}, app_id='')
        with assert_raises(RouterException):
            self.router.register("uaid",
                                 router_data={"token": "abcd1234"},
                                 app_id="invalid123")

    @patch("gcmclient.GCM")
    def test_gcmclient_fail(self, fgcm):
        fgcm.side_effect = Exception
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        with assert_raises(IOError):
            GCMRouter(settings, {"senderIDs": {"test123": {"auth": "abcd"}}})

    def test_route_notification(self):
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            ok_(self.router.gcm['test123'].send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm['test123'].send.call_args[0][0].data
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['chid'], dummy_chid)
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')

        d.addCallback(check_results)
        return d

    def test_ttl_none(self):
        self.router.gcm['test123'] = self.gcm
        self.notif = WebPushNotification(uaid=uuid.UUID(dummy_uaid),
                                         channel_id=uuid.UUID(dummy_chid),
                                         data="q60d6g",
                                         headers=self.headers,
                                         ttl=None)
        self.notif.cleanup_headers()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.status_code, 201)
            eq_(result.logged_status, 200)
            ok_("TTL" in result.headers)
            ok_(self.router.gcm['test123'].send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm['test123'].send.call_args[0][0].data
            options = self.router.gcm['test123'].send.call_args[0][0].options
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['chid'], dummy_chid)
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
            # use the defined min TTL
            eq_(options['time_to_live'], 60)

        d.addCallback(check_results)
        return d

    def test_ttl_high(self):
        self.router.gcm['test123'] = self.gcm
        self.notif = WebPushNotification(uaid=uuid.UUID(dummy_uaid),
                                         channel_id=uuid.UUID(dummy_chid),
                                         data="q60d6g",
                                         headers=self.headers,
                                         ttl=5184000)
        self.notif.cleanup_headers()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            ok_(self.router.gcm['test123'].send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm['test123'].send.call_args[0][0].data
            options = self.router.gcm['test123'].send.call_args[0][0].options
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['chid'], dummy_chid)
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
            # use the defined min TTL
            eq_(options['time_to_live'], 2419200)

        d.addCallback(check_results)
        return d

    def test_long_data(self):
        self.router.gcm['test123'] = self.gcm
        bad_notif = WebPushNotification(
            uaid=uuid.UUID(dummy_uaid),
            channel_id=uuid.UUID(dummy_chid),
            data="\x01abcdefghijklmnopqrstuvwxyz0123456789",
            headers=self.headers,
            ttl=200)

        d = self.router.route_notification(bad_notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 413)
            eq_(result.value.errno, 104)

        d.addBoth(check_results)
        return d

    def test_route_crypto_notification(self):
        self.router.gcm['test123'] = self.gcm
        del (self.notif.headers['encryption_key'])
        self.notif.headers['crypto_key'] = 'crypto'
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            ok_(self.router.gcm['test123'].send.called)

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()

        self.gcm.send.side_effect = throw_auth
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500, "Server error")

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")

        self.gcm.send.side_effect = throw_other
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500, "Server error")

        d.addBoth(check_results)
        return d

    def test_router_notification_connection_error(self):
        from requests.exceptions import ConnectionError

        def throw_other(*args, **kwargs):
            raise ConnectionError("oh my!")

        self.gcm.send.side_effect = throw_other
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 502, "Server error")

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            ok_(self.router.gcm['test123'].send.called)

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            ok_(self.router.gcm['test123'].send.called)

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        self.router.gcm['test123'] = self.gcm
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            ok_(self.router.metrics.increment.called)
            eq_(self.router.metrics.increment.call_args[0][0],
                'updates.client.bridge.gcm.failed.failure')
            eq_(fail.value.message, 'GCM unable to deliver')
            self._check_error_call(fail.value, 410)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        self.router.gcm['test123'] = self.gcm
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            ok_(self.router.metrics.increment.called)
            eq_(self.router.metrics.increment.call_args[0][0],
                'updates.client.bridge.gcm.failed.retry')
            eq_(fail.value.message, 'GCM failure to deliver, retry')
            self._check_error_call(fail.value, 503)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {
                                               "token": "abc"
                                           }})

        def check_results(fail):
            eq_(fail.value.status_code, 500, "Server error")

        d.addBoth(check_results)
        return d

    def test_amend(self):
        router_data = {"token": "test123"}
        self.router.register("uaid", router_data=router_data, app_id="test123")
        resp = {"key": "value"}
        self.router.amend_endpoint_response(
            resp, self.router_data.get('router_data'))
        eq_({"key": "value", "senderid": "test123"}, resp)

    def test_register_invalid_token(self):
        with assert_raises(RouterException):
            self.router.register(uaid="uaid",
                                 router_data={"token": "invalid"},
                                 app_id="invalid")
Beispiel #14
0
    def __init__(
        self,
        crypto_key=None,
        datadog_api_key=None,
        datadog_app_key=None,
        datadog_flush_interval=None,
        hostname=None,
        port=None,
        router_scheme=None,
        router_hostname=None,
        router_port=None,
        endpoint_scheme=None,
        endpoint_hostname=None,
        endpoint_port=None,
        router_conf=None,
        router_tablename="router",
        router_read_throughput=5,
        router_write_throughput=5,
        storage_tablename="storage",
        storage_read_throughput=5,
        storage_write_throughput=5,
        message_tablename="message",
        message_read_throughput=5,
        message_write_throughput=5,
        statsd_host="localhost",
        statsd_port=8125,
        resolve_hostname=False,
        max_data=4096,
        # Reflected up from UDP Router
        wake_timeout=0,
        env='development',
        enable_cors=False,
        hello_timeout=0,
        bear_hash_key=None,
        preflight_uaid="deadbeef00000000deadbeef00000000",
        ami_id=None,
        client_certs=None,
        msg_limit=100,
        debug=False,
        connect_timeout=0.5,
    ):
        """Initialize the Settings object

        Upon creation, the HTTP agent will initialize, all configured routers
        will be setup and started, logging will be started, and the database
        will have a preflight check done.

        """
        # Use a persistent connection pool for HTTP requests.
        pool = HTTPConnectionPool(reactor)
        if not debug:
            pool._factory = QuietClientFactory

        self.agent = Agent(reactor, connectTimeout=connect_timeout, pool=pool)

        if not crypto_key:
            crypto_key = [Fernet.generate_key()]
        if not isinstance(crypto_key, list):
            crypto_key = [crypto_key]
        self.update(crypto_key=crypto_key)
        self.crypto_key = crypto_key

        if bear_hash_key is None:
            bear_hash_key = []
        if not isinstance(bear_hash_key, list):
            bear_hash_key = [bear_hash_key]
        self.bear_hash_key = bear_hash_key

        self.max_data = max_data
        self.clients = {}

        # Setup hosts/ports/urls
        default_hostname = socket.gethostname()
        self.hostname = hostname or default_hostname
        if resolve_hostname:
            self.hostname = resolve_ip(self.hostname)

        # Metrics setup
        if datadog_api_key:
            self.metrics = DatadogMetrics(
                hostname=self.hostname,
                api_key=datadog_api_key,
                app_key=datadog_app_key,
                flush_interval=datadog_flush_interval,
            )
        elif statsd_host:
            self.metrics = TwistedMetrics(statsd_host, statsd_port)
        else:
            self.metrics = SinkMetrics()

        self.port = port
        self.endpoint_hostname = endpoint_hostname or self.hostname
        self.router_hostname = router_hostname or self.hostname

        if router_conf is None:
            router_conf = {}
        self.router_conf = router_conf
        self.router_url = canonical_url(router_scheme or 'http',
                                        self.router_hostname, router_port)

        self.endpoint_url = canonical_url(endpoint_scheme or 'http',
                                          self.endpoint_hostname,
                                          endpoint_port)
        self.enable_tls_auth = client_certs is not None
        self.client_certs = client_certs

        # Database objects
        self.router_table = get_router_table(router_tablename,
                                             router_read_throughput,
                                             router_write_throughput)
        self.storage_table = get_storage_table(storage_tablename,
                                               storage_read_throughput,
                                               storage_write_throughput)
        self.message_table = get_rotating_message_table(
            message_tablename,
            message_read_throughput=message_read_throughput,
            message_write_throughput=message_write_throughput)
        self._message_prefix = message_tablename
        self.message_limit = msg_limit
        self.storage = Storage(self.storage_table, self.metrics)
        self.router = Router(self.router_table, self.metrics)

        # Used to determine whether a connection is out of date with current
        # db objects. There are three noteworty cases:
        # 1 "Last Month" the table requires a rollover.
        # 2 "This Month" the most common case.
        # 3 "Next Month" where the system will soon be rolling over, but with
        #   timing, some nodes may roll over sooner. Ensuring the next month's
        #   table is present before the switchover is the main reason for this,
        #   just in case some nodes do switch sooner.
        self.create_initial_message_tables()

        # Run preflight check
        preflight_check(self.storage, self.router, preflight_uaid)

        # CORS
        self.cors = enable_cors

        # Force timeout in idle seconds
        self.wake_timeout = wake_timeout

        # Setup the routers
        self.routers = dict()
        self.routers["simplepush"] = SimpleRouter(
            self, router_conf.get("simplepush"))
        self.routers["webpush"] = WebPushRouter(self, None)
        if 'apns' in router_conf:
            self.routers["apns"] = APNSRouter(self, router_conf["apns"])
        if 'gcm' in router_conf:
            self.routers["gcm"] = GCMRouter(self, router_conf["gcm"])

        # Env
        self.env = env

        self.hello_timeout = hello_timeout

        self.ami_id = ami_id

        # Generate messages per legacy rules, only used for testing to
        # generate legacy data.
        self._notification_legacy = False
Beispiel #15
0
class GCMRouterTestCase(unittest.TestCase):

    @patch("gcmclient.gcm.GCM", spec=gcmclient.gcm.GCM)
    def setUp(self, fgcm):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        self.gcm_config = {'s3_bucket': 'None',
                           'max_data': 32,
                           'senderid_list': {'test123':
                                             {"auth": "12345678abcdefg"}}}
        self.gcm = fgcm
        self.router = GCMRouter(settings, self.gcm_config)
        self.headers = {"content-encoding": "aesgcm",
                        "encryption": "test",
                        "encryption-key": "test"}
        # Data will most likely be binary values.
        self.notif = Notification(10, "\xab\xad\x1d\xea", dummy_chid,
                                  self.headers, 200)
        self.router_data = dict(
            router_data=dict(
                token="connect_data",
                creds=dict(senderID="test123", auth="12345678abcdefg")))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        fgcm.send.return_value = mock_result

    def tearDown(self):
        self.router.senderIDs.stop()

    def _check_error_call(self, exc, code):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        assert(self.router.gcm.send.called)
        self.flushLoggedErrors()

    def test_init(self):
        self.router.senderIDs.get_ID = Mock()

        def throw_ex():
            raise AttributeError
        fsenderids = Mock()
        fsenderids.choose_ID.side_effect = throw_ex
        self.assertRaises(IOError, GCMRouter, {}, {"senderIDs": fsenderids})

    def test_register(self):
        result = self.router.register("uaid", {"token": "connect_data"})
        # Check the information that will be recorded for this user
        eq_(result, {"token": "connect_data",
                     "creds": {"senderID": "test123",
                               "auth": "12345678abcdefg"}})

    def test_register_bad(self):
        self.assertRaises(RouterException, self.router.register, "uaid", {})

    def test_invalid_token(self):
        self.router.gcm = self.gcm

        (t, v) = self.router.check_token("test123")
        ok_(t)
        eq_(v, self.gcm_config['senderid_list'].keys()[0])

        (t, v) = self.router.check_token("invalid")
        eq_(t, False)
        eq_(v, self.gcm_config['senderid_list'].keys()[0])

    def test_route_notification(self):
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm.send.call_args[0][0].data
            eq_(data['body'], 'q60d6g==')
            eq_(data['enc'], 'test')
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
        d.addCallback(check_results)
        return d

    def test_long_data(self):
        self.router.gcm = self.gcm
        badNotif = Notification(
            10, "\x01abcdefghijklmnopqrstuvwxyz0123456789", dummy_chid,
            self.headers, 200)
        d = self.router.route_notification(badNotif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 413)
            eq_(result.value.errno, 104)

        d.addBoth(check_results)
        return d

    def test_route_crypto_notification(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['encryption-key'])
        self.notif.headers['crypto-key'] = 'crypto'
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_missing_enc_key_header(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['encryption-key'])
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 400)

        d.addBoth(check_results)
        return d

    def test_router_bogus_headers(self):
        self.router.gcm = self.gcm
        self.notif.headers['crypto-key'] = "crypto"
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 400)

        d.addBoth(check_results)
        return d

    def test_router_missing_enc_header(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['encryption'])
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 400)

        d.addBoth(check_results)
        return d

    def test_router_missing_cont_enc_header(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['content-encoding'])
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 400)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()
        self.gcm.send.side_effect = throw_auth
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")
        self.gcm.send.side_effect = throw_other
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {"token": "abc"}})

        def check_results(fail):
            eq_(fail.value.status_code, 500)
        d.addBoth(check_results)
        return d

    def test_ammend(self):
        self.router.register("uaid", {"token": "connect_data"})
        resp = {"key": "value"}
        result = self.router.amend_msg(resp,
                                       self.router_data.get('router_data'))
        eq_({"key": "value", "senderid": "test123"},
            result)
Beispiel #16
0
class GCMRouterTestCase(unittest.TestCase):

    @patch("gcmclient.gcm.GCM", spec=gcmclient.gcm.GCM)
    def setUp(self, fgcm):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        self.gcm_config = {'s3_bucket': 'None',
                           'max_data': 32,
                           'ttl': 60,
                           'senderid_list': {'test123':
                                             {"auth": "12345678abcdefg"}}}
        self.gcm = fgcm
        self.router = GCMRouter(settings, self.gcm_config)
        self.headers = {"content-encoding": "aesgcm",
                        "encryption": "test",
                        "encryption-key": "test"}
        # Payloads are Base64-encoded.
        self.notif = Notification(10, "q60d6g", dummy_chid, self.headers,
                                  200)
        self.router_data = dict(
            router_data=dict(
                token="connect_data",
                creds=dict(senderID="test123", auth="12345678abcdefg")))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        fgcm.send.return_value = mock_result

    def tearDown(self):
        self.router.senderIDs.stop()

    def _check_error_call(self, exc, code):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        assert(self.router.gcm.send.called)
        self.flushLoggedErrors()

    def test_init(self):
        self.router.senderIDs.get_ID = Mock()

        def throw_ex():
            raise AttributeError
        fsenderids = Mock()
        fsenderids.choose_ID.side_effect = throw_ex
        self.assertRaises(IOError, GCMRouter, {}, {"senderIDs": fsenderids})

    def test_register(self):
        result = self.router.register("uaid", {"token": "connect_data"})
        # Check the information that will be recorded for this user
        eq_(result, {"token": "connect_data",
                     "creds": {"senderID": "test123",
                               "auth": "12345678abcdefg"}})

    def test_register_bad(self):
        self.assertRaises(RouterException, self.router.register, "uaid", {})

    def test_invalid_token(self):
        self.router.gcm = self.gcm

        (t, v) = self.router.check_token("test123")
        ok_(t)
        eq_(v, self.gcm_config['senderid_list'].keys()[0])

        (t, v) = self.router.check_token("invalid")
        eq_(t, False)
        eq_(v, self.gcm_config['senderid_list'].keys()[0])

    def test_route_notification(self):
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm.send.call_args[0][0].data
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
        d.addCallback(check_results)
        return d

    def test_ttl_none(self):
        self.router.gcm = self.gcm
        self.notif = Notification(version=10,
                                  data="q60d6g",
                                  channel_id=dummy_chid,
                                  headers=self.headers,
                                  ttl=None)
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm.send.call_args[0][0].data
            options = self.router.gcm.send.call_args[0][0].options
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
            # use the defined min TTL
            eq_(options['time_to_live'], 60)
        d.addCallback(check_results)
        return d

    def test_long_data(self):
        self.router.gcm = self.gcm
        badNotif = Notification(
            10, "\x01abcdefghijklmnopqrstuvwxyz0123456789", dummy_chid,
            self.headers, 200)
        d = self.router.route_notification(badNotif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 413)
            eq_(result.value.errno, 104)

        d.addBoth(check_results)
        return d

    def test_route_crypto_notification(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['encryption-key'])
        self.notif.headers['crypto-key'] = 'crypto'
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()
        self.gcm.send.side_effect = throw_auth
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")
        self.gcm.send.side_effect = throw_other
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {"token": "abc"}})

        def check_results(fail):
            eq_(fail.value.status_code, 500)
        d.addBoth(check_results)
        return d

    def test_ammend(self):
        self.router.register("uaid", {"token": "connect_data"})
        resp = {"key": "value"}
        result = self.router.amend_msg(resp,
                                       self.router_data.get('router_data'))
        eq_({"key": "value", "senderid": "test123"},
            result)
Beispiel #17
0
class GCMRouterTestCase(unittest.TestCase):
    def setUp(self):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        # Mock out GCM client
        self._old_gcm = gcmclient.GCM
        gcmclient.GCM = Mock(spec=gcmclient.GCM)

        gcm_config = {'apikey': '12345678abcdefg'}
        self.router = GCMRouter(settings, gcm_config)
        self.notif = Notification(10, "data", dummy_chid)
        self.router_data = dict(router_data=dict(token="connect_data"))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        self.router.gcm.send.return_value = mock_result

    def tearDown(self):
        gcmclient.GCM = self._old_gcm

    def _check_error_call(self, exc, code):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        self.router.gcm.send.assert_called()
        self.flushLoggedErrors()

    def test_register(self):
        result = self.router.register("uaid", {"token": "connect_data"})
        eq_(result, {"token": "connect_data"})

    def test_register_bad(self):
        self.assertRaises(RouterException, self.router.register, "uaid", {})

    def test_router_notification(self):
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            self.router.gcm.send.assert_called()
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()
        self.router.gcm.send.side_effect = throw_auth
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")
        self.router.gcm.send.side_effect = throw_other
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            self.router.gcm.send.assert_called()
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            self.router.gcm.send.assert_called()
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d