diff --git a/devproxy/handlers/wurfl_handler/base.py b/devproxy/handlers/wurfl_handler/base.py index 7712915..39810b4 100644 --- a/devproxy/handlers/wurfl_handler/base.py +++ b/devproxy/handlers/wurfl_handler/base.py @@ -1,9 +1,10 @@ import hashlib import json -from twisted.internet.defer import inlineCallbacks, returnValue, succeed -from twisted.internet import protocol, reactor -from twisted.protocols.memcache import MemCacheProtocol, DEFAULT_PORT +from twisted.internet.defer import (inlineCallbacks, returnValue, succeed, + Deferred) +from twisted.internet import reactor +from twisted.protocols.memcache import DEFAULT_PORT from twisted.web.template import flattenString from pywurfl.algorithms import TwoStepAnalysis @@ -11,6 +12,7 @@ from devproxy.handlers.base import BaseHandler from devproxy.handlers.wurfl_handler import wurfl_devices from devproxy.handlers.wurfl_handler import debug +from devproxy.utils.memcached import ReconnectingMemCacheClientFactory class WurflHandlerException(Exception): @@ -29,23 +31,30 @@ def validate_config(self, config): def setup_handler(self): self.devices = wurfl_devices.devices self.algorithm = TwoStepAnalysis(self.devices) - self.memcached = yield self.connect_to_memcached( - **self.memcached_config) + yield self.connect_to_memcached(**self.memcached_config) self.namespace = yield self.get_namespace() returnValue(self) def connect_to_memcached(self, host="localhost", port=DEFAULT_PORT): - creator = protocol.ClientCreator(reactor, MemCacheProtocol) + self.memcached_factory = ReconnectingMemCacheClientFactory() + reactor.connectTCP(host, port, self.memcached_factory) - def eb(failure): - raise WurflHandlerException( - 'Unable to connect to memcached on %s:%s' % ( - host, port), failure) + d = Deferred() + + def cb(): + if hasattr(self.memcached_factory, 'client'): + d.callback(self.memcached_factory.client) + else: + reactor.callLater(0, cb) + + cb() - d = creator.connectTCP(host, port) - d.addErrback(eb) return d + @property + def memcached(self): + return self.memcached_factory.client + def get_namespace_key(self): return '%s_namespace' % (self.cache_prefix,) diff --git a/devproxy/handlers/wurfl_handler/scientia_mobile_cloud.py b/devproxy/handlers/wurfl_handler/scientia_mobile_cloud.py index a4e7dab..aef17cd 100644 --- a/devproxy/handlers/wurfl_handler/scientia_mobile_cloud.py +++ b/devproxy/handlers/wurfl_handler/scientia_mobile_cloud.py @@ -27,7 +27,7 @@ def validate_config(self, config): @inlineCallbacks def setup_handler(self): - self.memcached = yield self.connect_to_memcached( + yield self.connect_to_memcached( **self.memcached_config) self.namespace = yield self.get_namespace() returnValue(self) diff --git a/devproxy/tests/test_wurfl_handler.py b/devproxy/tests/test_wurfl_handler.py index 553e9b9..5b0791e 100644 --- a/devproxy/tests/test_wurfl_handler.py +++ b/devproxy/tests/test_wurfl_handler.py @@ -1,6 +1,6 @@ import hashlib -from twisted.internet.defer import inlineCallbacks +from twisted.internet.defer import inlineCallbacks, succeed from devproxy.handlers.wurfl_handler.simple import SimpleWurflHandler from devproxy.utils import http @@ -13,8 +13,9 @@ class WurlfHandlerTestCase(ProxyTestCase): def setUp(self): yield super(WurlfHandlerTestCase, self).setUp() self.fake_memcached = FakeMemcached() + self.patch(SimpleWurflHandler, 'memcached', self.fake_memcached) self.patch(SimpleWurflHandler, 'connect_to_memcached', - self.patch_memcached) + lambda _: succeed(True)) self.wurfl_handlers = yield self.start_handlers([SimpleWurflHandler({ 'header_name': 'X-UA-header', @@ -24,9 +25,6 @@ def setUp(self): 'debug_path': '/_debug', })]) - def patch_memcached(self, **config): - return self.fake_memcached - @inlineCallbacks def test_wurfl_nokia_lookup(self): proxy, url = self.start_proxy(self.wurfl_handlers) diff --git a/devproxy/tests/test_wurfl_scientia_mobile_cloud.py b/devproxy/tests/test_wurfl_scientia_mobile_cloud.py index 4edc4c7..f7322de 100644 --- a/devproxy/tests/test_wurfl_scientia_mobile_cloud.py +++ b/devproxy/tests/test_wurfl_scientia_mobile_cloud.py @@ -19,8 +19,10 @@ def setUp(self): 'get_device_from_smcloud', self.patch_get_device_from_smcloud) self.patch(ScientiaMobileCloudResolutionHandler, - 'connect_to_memcached', - self.patch_memcached) + 'memcached', self.fake_memcached) + self.patch(ScientiaMobileCloudResolutionHandler, + 'connect_to_memcached', lambda _: succeed(True)) + self.wurfl_handlers = yield self.start_handlers([ ScientiaMobileCloudResolutionHandler({ 'header_name': 'X-UA-header', @@ -51,9 +53,6 @@ def mock_response(self, user_agent, json_device): def patch_get_device_from_smcloud(self, user_agent): return succeed(self._mocked_devices.get(user_agent, {})) - def patch_memcached(self, **config): - return self.fake_memcached - @inlineCallbacks def test_wurfl_nokia_lookup(self): proxy, url = self.start_proxy(self.wurfl_handlers) diff --git a/devproxy/utils/memcached.py b/devproxy/utils/memcached.py new file mode 100644 index 0000000..9ce96f7 --- /dev/null +++ b/devproxy/utils/memcached.py @@ -0,0 +1,14 @@ +from twisted.internet import protocol +from twisted.protocols.memcache import MemCacheProtocol + + +class ReconnectingMemCacheClientFactory(protocol.ReconnectingClientFactory): + + protocol = MemCacheProtocol + + def buildProtocol(self, addr): + self.client = self.protocol() + self.addr = addr + self.client.factory = self + self.resetDelay() + return self.client