1e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved. 2e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 3e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)# found in the LICENSE file. 4e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 5e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import calendar 6e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import datetime 7e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import json 87242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucciimport logging 9e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import webapp2 10e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)import zlib 11e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 12e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)from google.appengine.api import memcache 13e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 147242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano TucciLOGGER = logging.getLogger(__name__) 157242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 16e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 17e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)class DateTimeEncoder(json.JSONEncoder): 18e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) def default(self, obj): 19e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) if isinstance(obj, datetime.datetime): 20e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) return calendar.timegm(obj.timetuple()) 217242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci # Let the base class default method raise the TypeError. 22e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) return json.JSONEncoder.default(self, obj) 23e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 24e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 25e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)class AlertsHandler(webapp2.RequestHandler): 26e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) MEMCACHE_ALERTS_KEY = 'alerts' 27e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 287242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci # Has no 'response' member. 297242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci # pylint: disable=E1101 307242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def send_json_headers(self): 31e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) self.response.headers.add_header('Access-Control-Allow-Origin', '*') 32e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) self.response.headers['Content-Type'] = 'application/json' 337242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 347242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci # Has no 'response' member. 357242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci # pylint: disable=E1101 367242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def send_json_data(self, data): 377242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self.send_json_headers() 387242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self.response.write(data) 397242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 407242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def generate_json_dump(self, alerts): 417242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci return json.dumps(alerts, cls=DateTimeEncoder, indent=1) 427242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 437242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def get_from_memcache(self, memcache_key): 447242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci compressed = memcache.get(memcache_key) 45e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) if not compressed: 467242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self.send_json_headers() 47e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) return 48e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) uncompressed = zlib.decompress(compressed) 497242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self.send_json_data(uncompressed) 50e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 517242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def get(self): 527242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self.get_from_memcache(AlertsHandler.MEMCACHE_ALERTS_KEY) 537242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 547242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci # Has no 'response' member. 557242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci # pylint: disable=E1101 567242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def post_to_memcache(self, memcache_key): 57e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) try: 58e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) alerts = json.loads(self.request.get('content')) 59e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) except ValueError: 607242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci warning = 'content field was not JSON' 617242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self.response.set_status(400, warning) 627242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci LOGGER.warn(warning) 63e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) return 64e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) alerts.update({ 65e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 'date': datetime.datetime.utcnow(), 66e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 'alerts': alerts['alerts'] 67e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) }) 687242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci uncompressed = self.generate_json_dump(alerts) 69e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) compression_level = 1 70e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) compressed = zlib.compress(uncompressed, compression_level) 717242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci memcache.set(memcache_key, compressed) 727242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 737242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def post(self): 747242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self.post_to_memcache(AlertsHandler.MEMCACHE_ALERTS_KEY) 75e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 76e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 77e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)app = webapp2.WSGIApplication([ 78e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) ('/alerts', AlertsHandler) 79e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)]) 80