From a794e20fa82075d72644d0f358951cb5d0fed17b Mon Sep 17 00:00:00 2001 From: Philip Jenvey Date: Fri, 26 Aug 2016 14:14:47 -0700 Subject: [PATCH] refactor: extract a simple BaseHandler --- autopush/base.py | 46 ++++++++++++++++++++++++++++ autopush/endpoint.py | 24 +++------------ autopush/health.py | 14 ++------- autopush/log_check.py | 3 -- autopush/tests/test_endpoint.py | 6 ++-- autopush/tests/test_web_base.py | 14 ++++----- autopush/tests/test_web_webpush.py | 6 ++-- autopush/utils.py | 25 ---------------- autopush/web/base.py | 48 +++--------------------------- autopush/web/simplepush.py | 4 +-- autopush/web/webpush.py | 4 +-- autopush/websocket.py | 13 ++------ 12 files changed, 76 insertions(+), 131 deletions(-) create mode 100644 autopush/base.py diff --git a/autopush/base.py b/autopush/base.py new file mode 100644 index 00000000..28212e7b --- /dev/null +++ b/autopush/base.py @@ -0,0 +1,46 @@ +import uuid + +import cyclone.web +from twisted.logger import Logger +from twisted.python import failure + + +class BaseHandler(cyclone.web.RequestHandler): + """Base cyclone RequestHandler for autopush""" + + log = Logger() + + def initialize(self, ap_settings): + """Setup basic attributes from AutopushSettings""" + self.ap_settings = ap_settings + self._client_info = self._init_info() + + def _init_info(self): + return dict( + ami_id=self.ap_settings.ami_id, + request_id=str(uuid.uuid4()), + user_agent=self.request.headers.get('user-agent', ""), + remote_ip=self.request.headers.get('x-forwarded-for', + self.request.remote_ip), + authorization=self.request.headers.get('authorization', ""), + message_ttl=self.request.headers.get('ttl', ""), + ) + + def write_error(self, code, **kwargs): + """Write the error (otherwise unhandled exception when dealing with + unknown method specifications.) + + This is a Cyclone API Override method used by endpoint and + websocket. + + """ + self.set_status(code) + if 'exc_info' in kwargs: + self.log.failure( + format=kwargs.get('format', "Exception"), + failure=failure.Failure(*kwargs['exc_info']), + **self._client_info) + else: + self.log.failure("Error in handler: %s" % code, + **self._client_info) + self.finish() diff --git a/autopush/endpoint.py b/autopush/endpoint.py index 034540ca..3d0cf7a3 100644 --- a/autopush/endpoint.py +++ b/autopush/endpoint.py @@ -41,6 +41,7 @@ from twisted.internet.defer import Deferred from twisted.internet.threads import deferToThread +from autopush.base import BaseHandler from autopush.db import ( generate_last_connect, hasher, @@ -51,7 +52,6 @@ from autopush.utils import ( generate_hash, validate_uaid, - ErrorLogger, extract_jwt, base64url_encode, ) @@ -119,7 +119,7 @@ def parse_request_params(request): return version, data -class AutoendpointHandler(ErrorLogger, cyclone.web.RequestHandler): +class AutoendpointHandler(BaseHandler): """Common overrides for Autoendpoint handlers""" cors_methods = "" cors_request_headers = () @@ -130,13 +130,9 @@ class AutoendpointHandler(ErrorLogger, cyclone.web.RequestHandler): ############################################################# def initialize(self, ap_settings): """Setup basic aliases and attributes""" - self.uaid_hash = "" - self._uaid = "" - self._chid = "" - self.ap_settings = ap_settings + super(AutoendpointHandler, self).initialize(ap_settings) + self.uaid_hash = self._uaid = self._chid = "" self.metrics = ap_settings.metrics - self.request_id = str(uuid.uuid4()) - self._client_info = self._init_info() def prepare(self): """Common request preparation""" @@ -184,18 +180,6 @@ def chid(self, value): self._chid = normalize_id(value) self._client_info["channelID"] = self._chid - def _init_info(self): - """Returns a dict of additional client data""" - return { - "ami_id": self.ap_settings.ami_id, - "request_id": self.request_id, - "user_agent": self.request.headers.get("user-agent", ""), - "remote_ip": self.request.headers.get("x-forwarded-for", - self.request.remote_ip), - "authorization": self.request.headers.get("authorization", ""), - "message_ttl": self.request.headers.get("ttl", ""), - } - ############################################################# # Error Callbacks ############################################################# diff --git a/autopush/health.py b/autopush/health.py index 9d4671a5..8541762c 100644 --- a/autopush/health.py +++ b/autopush/health.py @@ -1,13 +1,12 @@ """Health Check HTTP Handler""" import cyclone.web -from autopush.endpoint import AutoendpointHandler +from autopush.base import BaseHandler from boto.dynamodb2.exceptions import ( InternalServerError, ) from twisted.internet.defer import DeferredList from twisted.internet.threads import deferToThread -from twisted.logger import Logger from autopush import __version__ @@ -17,12 +16,8 @@ class MissingTableException(Exception): pass -class HealthHandler(AutoendpointHandler): +class HealthHandler(BaseHandler): """HTTP Health Handler""" - log = Logger() - - def initialize(self, ap_settings): - self.ap_settings = ap_settings @cyclone.web.asynchronous def get(self): @@ -83,12 +78,9 @@ def _finish_response(self, results): self.finish() -class StatusHandler(cyclone.web.RequestHandler): +class StatusHandler(BaseHandler): """HTTP Status Handler""" - def initialize(self, ap_settings): - self.ap_settings = ap_settings - def get(self): """HTTP Get diff --git a/autopush/log_check.py b/autopush/log_check.py index b27fa575..e8c32248 100644 --- a/autopush/log_check.py +++ b/autopush/log_check.py @@ -1,5 +1,3 @@ -import uuid - import cyclone.web from autopush.endpoint import AutoendpointHandler @@ -13,7 +11,6 @@ class LogCheckHandler(AutoendpointHandler): def initialize(self, ap_settings): self.ap_settings = ap_settings - self.request_id = str(uuid.uuid4()) self._client_info = self._init_info() @cyclone.web.asynchronous diff --git a/autopush/tests/test_endpoint.py b/autopush/tests/test_endpoint.py index 54889193..61e24cf7 100644 --- a/autopush/tests/test_endpoint.py +++ b/autopush/tests/test_endpoint.py @@ -162,8 +162,7 @@ class EndpointTestCase(unittest.TestCase): ["location", "www-authenticate"] ) - @patch('uuid.uuid4', return_value=uuid.UUID(dummy_request_id)) - def setUp(self, t): + def setUp(self): from twisted.logger import Logger # this timeout *should* be set to 0.5, however Travis runs # so slow, that many of these tests will time out leading @@ -454,7 +453,8 @@ def handle_finish(value): self.finish_deferred.addCallback(handle_finish) return self.finish_deferred - def test_init_info(self): + @patch('uuid.uuid4', return_value=uuid.UUID(dummy_request_id)) + def test_init_info(self, t): d = self.endpoint._init_info() eq_(d["request_id"], dummy_request_id) h = self.request_mock.headers diff --git a/autopush/tests/test_web_base.py b/autopush/tests/test_web_base.py index fa364bf3..25f1a4cc 100644 --- a/autopush/tests/test_web_base.py +++ b/autopush/tests/test_web_base.py @@ -46,9 +46,8 @@ class TestBase(unittest.TestCase): ["location", "www-authenticate"] ) - @patch('uuid.uuid4', return_value=uuid.UUID(dummy_request_id)) - def setUp(self, t): - from autopush.web.base import BaseHandler + def setUp(self): + from autopush.web.base import BaseWebHandler settings = AutopushSettings( hostname="localhost", @@ -59,9 +58,9 @@ def setUp(self, t): headers={"ttl": "0"}, host='example.com:8080') - self.base = BaseHandler(Application(), - self.request_mock, - ap_settings=settings) + self.base = BaseWebHandler(Application(), + self.request_mock, + ap_settings=settings) self.status_mock = self.base.set_status = Mock() self.write_mock = self.base.write = Mock() self.base.log = Mock(spec=Logger) @@ -157,7 +156,8 @@ def test_write_error_no_exc(self): self.status_mock.assert_called_with(999) eq_(self.base.log.failure.called, True) - def test_init_info(self): + @patch('uuid.uuid4', return_value=uuid.UUID(dummy_request_id)) + def test_init_info(self, t): h = self.request_mock.headers h["user-agent"] = "myself" self.request_mock.remote_ip = "local1" diff --git a/autopush/tests/test_web_webpush.py b/autopush/tests/test_web_webpush.py index ff253942..fa9d6515 100644 --- a/autopush/tests/test_web_webpush.py +++ b/autopush/tests/test_web_webpush.py @@ -2,7 +2,7 @@ from cryptography.fernet import Fernet from cyclone.web import Application -from mock import Mock, patch +from mock import Mock from moto import mock_dynamodb2 from nose.tools import eq_ from twisted.internet.defer import Deferred @@ -16,7 +16,6 @@ from autopush.router.interface import IRouter, RouterResponse from autopush.settings import AutopushSettings -dummy_request_id = "11111111123412341234567812345678" dummy_uaid = str(uuid.UUID("abad1dea00000000aabbccdd00000000")) dummy_chid = str(uuid.UUID("deadbeef00000000decafbad00000000")) dummy_token = dummy_uaid + ":" + dummy_chid @@ -33,8 +32,7 @@ def tearDown(): class TestWebpushHandler(unittest.TestCase): - @patch('uuid.uuid4', return_value=uuid.UUID(dummy_request_id)) - def setUp(self, t): + def setUp(self): from autopush.web.webpush import WebPushHandler settings = AutopushSettings( diff --git a/autopush/utils.py b/autopush/utils.py index 4b8bfaec..7edcd89a 100644 --- a/autopush/utils.py +++ b/autopush/utils.py @@ -9,8 +9,6 @@ import ecdsa import requests from jose import jws -from twisted.logger import Logger -from twisted.python import failure from ua_parser import user_agent_parser @@ -196,26 +194,3 @@ def parse_user_agent(agent_string): raw_info["ua_browser_ver"] = ".".join(filter(None, browser_bits)) return dd_info, raw_info - - -class ErrorLogger(object): - log = Logger() - - def write_error(self, code, **kwargs): - """Write the error (otherwise unhandled exception when dealing with - unknown method specifications.) - - This is a Cyclone API Override method used by endpoint and websocket. - - """ - self.set_status(code) - if "exc_info" in kwargs: - fmt = kwargs.get("format", "Exception") - self.log.failure( - format=fmt, - failure=failure.Failure(*kwargs["exc_info"]), - **self._client_info) - else: - self.log.failure("Error in handler: %s" % code, - **self._client_info) - self.finish() diff --git a/autopush/web/base.py b/autopush/web/base.py index 27d96643..b2fb17ef 100644 --- a/autopush/web/base.py +++ b/autopush/web/base.py @@ -1,16 +1,13 @@ import json import time -import uuid from collections import namedtuple -import cyclone.web from boto.dynamodb2.exceptions import ( ProvisionedThroughputExceededException, ) from boto.exception import BotoServerError -from twisted.logger import Logger -from twisted.python import failure +from autopush.base import BaseHandler from autopush.db import ( hasher, normalize_id, @@ -41,30 +38,23 @@ class Notification(namedtuple("Notification", class VapidAuthException(Exception): """Exception if the VAPID Auth token fails""" - pass -class BaseHandler(cyclone.web.RequestHandler): +class BaseWebHandler(BaseHandler): """Common overrides for Push web API's""" cors_methods = "" cors_request_headers = () cors_response_headers = () - log = Logger() - ############################################################# # Cyclone API Methods ############################################################# def initialize(self, ap_settings): """Setup basic aliases and attributes""" - self.uaid_hash = "" - self._uaid = "" - self._chid = "" + super(BaseWebHandler, self).initialize(ap_settings) + self.uaid_hash = self._uaid = self._chid = "" self.start_time = time.time() - self.ap_settings = ap_settings self.metrics = ap_settings.metrics - self.request_id = str(uuid.uuid4()) - self._client_info = self._init_info() def prepare(self): """Common request preparation""" @@ -77,25 +67,6 @@ def prepare(self): self.set_header("Access-Control-Expose-Headers", ",".join(self.cors_response_headers)) - def write_error(self, code, **kwargs): - """Write the error (otherwise unhandled exception when dealing with - unknown method specifications.) - - This is a Cyclone API Override. - - """ - self.set_status(code) - if "exc_info" in kwargs: - fmt = kwargs.get("format", "Exception") - self.log.failure( - format=fmt, - failure=failure.Failure(*kwargs["exc_info"]), - **self._client_info) - else: - self.log.failure("Error in handler: %s" % code, - **self._client_info) - self.finish() - ############################################################# # Cyclone HTTP Methods ############################################################# @@ -131,17 +102,6 @@ def chid(self, value): self._chid = normalize_id(value) self._client_info["channelID"] = self._chid - def _init_info(self): - """Returns a dict of additional client data""" - return { - "request_id": self.request_id, - "user_agent": self.request.headers.get("user-agent", ""), - "remote_ip": self.request.headers.get("x-forwarded-for", - self.request.remote_ip), - "authorization": self.request.headers.get("authorization", ""), - "message_ttl": self.request.headers.get("ttl", ""), - } - ############################################################# # Error Callbacks ############################################################# diff --git a/autopush/web/simplepush.py b/autopush/web/simplepush.py index 9bf21f1b..6c39a170 100644 --- a/autopush/web/simplepush.py +++ b/autopush/web/simplepush.py @@ -3,7 +3,7 @@ from twisted.internet.defer import Deferred from autopush.web.base import ( - BaseHandler, + BaseWebHandler, Notification, ) from autopush.web.validation import ( @@ -12,7 +12,7 @@ ) -class SimplePushHandler(BaseHandler): +class SimplePushHandler(BaseWebHandler): cors_methods = "PUT" @threaded_validate(SimplePushRequestSchema) diff --git a/autopush/web/webpush.py b/autopush/web/webpush.py index e344148c..30124626 100644 --- a/autopush/web/webpush.py +++ b/autopush/web/webpush.py @@ -4,7 +4,7 @@ from twisted.internet.threads import deferToThread from autopush.web.base import ( - BaseHandler, + BaseWebHandler, Notification, ) from autopush.web.validation import ( @@ -14,7 +14,7 @@ from autopush.websocket import ms_time -class WebPushHandler(BaseHandler): +class WebPushHandler(BaseWebHandler): cors_methods = "POST" cors_request_headers = ("content-encoding", "encryption", "crypto-key", "ttl", diff --git a/autopush/websocket.py b/autopush/websocket.py index 602d9e02..5e1b3d4e 100644 --- a/autopush/websocket.py +++ b/autopush/websocket.py @@ -36,7 +36,6 @@ from functools import wraps from random import randrange -import cyclone.web from autobahn.twisted.websocket import WebSocketServerProtocol from boto.dynamodb2.exceptions import ( ProvisionedThroughputExceededException, @@ -62,6 +61,7 @@ from zope.interface import implements from autopush import __version__ +from autopush.base import BaseHandler from autopush.db import ( has_connected_this_month, hasher, @@ -69,7 +69,6 @@ ) from autopush.protocol import IgnoreBody from autopush.utils import ( - ErrorLogger, parse_user_agent, validate_uaid, ) @@ -1304,16 +1303,13 @@ def send_notifications(self, update): self.sendJSON(msg) -class RouterHandler(cyclone.web.RequestHandler, ErrorLogger): +class RouterHandler(BaseHandler): """Router Handler Handles routing a notification to a connected client from an endpoint. """ - def initialize(self, ap_settings): - self.ap_settings = ap_settings - def put(self, uaid): """HTTP Put @@ -1341,10 +1337,7 @@ def put(self, uaid): self.write("Client accepted for delivery") -class NotificationHandler(cyclone.web.RequestHandler, ErrorLogger): - - def initialize(self, ap_settings): - self.ap_settings = ap_settings +class NotificationHandler(BaseHandler): def put(self, uaid, *args): """HTTP Put