def _output_if_modified(self, content):
        """
        Check for ETag, then fall back to If-Modified-Since
        """
        with TraceContext() as root:
            with root.span("CacheableHandler._output_if_modified"):
                modified = True

                # Normalize content
                try:
                    content = str(content)
                except UnicodeEncodeError:
                    content = unicode(content).encode('utf-8')

                etag = 'W/"{}"'.format(hashlib.md5(content).hexdigest())  # Weak ETag
                self.response.headers['ETag'] = etag

                if_none_match = self.request.headers.get('If-None-Match')
                if if_none_match and etag in [x.strip() for x in if_none_match.split(',')]:
                    self.response.set_status(304)
                    modified = False

                # Fall back to If-Modified-Since
                if modified and self._last_modified is not None:
                    last_modified = format_date_time(mktime(self._last_modified.timetuple()))
                    if_modified_since = self.request.headers.get('If-Modified-Since')
                    self.response.headers['Last-Modified'] = last_modified
                    if if_modified_since and if_modified_since == last_modified:
                        self.response.set_status(304)
                        modified = False

                if modified:
                    self.response.out.write(content)

                return modified
    def _validate_tba_auth_key(self):
        """
        Tests the presence of a X-TBA-Auth-Key header or URL param.
        """
        with TraceContext() as root:
            with root.span("ApiBaseController._validate_tba_auth_key"):
                x_tba_auth_key = self.request.headers.get("X-TBA-Auth-Key")
                if x_tba_auth_key is None:
                    x_tba_auth_key = self.request.get('X-TBA-Auth-Key')

                self.auth_owner = None
                self.auth_owner_key = None
                self.auth_description = None
                if not x_tba_auth_key:
                    account = self._user_bundle.account
                    if account:
                        self.auth_owner = account.key.id()
                        self.auth_owner_key = account.key
                    elif 'thebluealliance.com' in self.request.headers.get(
                            "Origin", ""):
                        self.auth_owner = 'The Blue Alliance'
                    else:
                        self._errors = {
                            "Error":
                            "X-TBA-Auth-Key is a required header or URL param. Please get an access key at http://www.thebluealliance.com/account."
                        }
                        self.abort(401)

                if self.auth_owner:
                    logging.info("Auth owner: {}, LOGGED IN".format(
                        self.auth_owner))
                else:
                    auth = ApiAuthAccess.get_by_id(x_tba_auth_key)
                    if auth and auth.is_read_key:
                        self.auth_owner = auth.owner.id()
                        self.auth_owner_key = auth.owner
                        self.auth_description = auth.description
                        if self.REQUIRE_ADMIN_AUTH and not auth.allow_admin:
                            self._errors = {
                                "Error":
                                "X-TBA-Auth-Key does not have required permissions"
                            }
                            self.abort(401)
                        logging.info(
                            "Auth owner: {}, X-TBA-Auth-Key: {}".format(
                                self.auth_owner, x_tba_auth_key))
                    else:
                        self._errors = {
                            "Error":
                            "X-TBA-Auth-Key is invalid. Please get an access key at http://www.thebluealliance.com/account."
                        }
                        self.abort(401)
    def get(self, *args, **kw):
        with TraceContext() as root:
            with root.span("ApiBaseController.get"):
                self._validate_tba_auth_key()
                self._errors = ValidationHelper.validate_request(self)
                if self._errors:
                    self.abort(404)

                super(ApiBaseController, self).get(*args, **kw)
                self.response.headers['X-TBA-Version'] = '{}'.format(
                    self.API_VERSION)
                self.response.headers['Vary'] = 'Accept-Encoding'

        if not self._errors:
            self._track_call(*args, **kw)
    def __init__(self, *args, **kw):
        super(CacheableHandler, self).__init__(*args, **kw)
        if type(self.request) == webapp2.Request:
            trace_context.request = self.request

        with TraceContext(sendTrace=False) as root:
            with root.span("CacheableHandler.__init__"):
                self._cache_expiration = 0
                self._last_modified = None  # A datetime object
                self._user_bundle = UserBundle()
                self._is_admin = self._user_bundle.is_current_user_admin
                if not hasattr(self, '_partial_cache_key'):
                    self._partial_cache_key = self.CACHE_KEY_FORMAT
                self.template_values = {}
                if self.response:
                    self.response.headers['Vary'] = 'Accept-Encoding'
    def get(self, *args, **kw):
        with TraceContext() as root:
            with root.span("CacheableHandler.get"):
                with root.span("CacheableHandler._read_cache"):
                    cached_response = self._read_cache()

                if cached_response is None:
                    self._set_cache_header_length(self.CACHE_HEADER_LENGTH)
                    self.template_values["render_time"] = datetime.datetime.now().replace(second=0, microsecond=0)  # Prevent ETag from changing too quickly
                    with root.span("CacheableHandler._render"):
                        rendered = self._render(*args, **kw)
                    if self._output_if_modified(self._add_admin_bar(rendered)):
                        self._write_cache(self.response)
                else:
                    self.response.headers.update(cached_response.headers)
                    del self.response.headers['Content-Length']  # Content-Length gets set automatically
                    self._output_if_modified(self._add_admin_bar(cached_response.body))
    def fetch_async(self, dict_version=None, return_updated=False):
        with TraceContext() as root:
            with root.span("{}.fetch_async".format(
                    self.__class__.__name__)) as spn:
                if dict_version:
                    if dict_version not in self.VALID_DICT_VERSIONS:
                        raise Exception(
                            "Bad api version for database query: {}".format(
                                dict_version))
                    cache_key = self._dict_cache_key(self.cache_key,
                                                     dict_version)
                else:
                    cache_key = self.cache_key

                cached_query = yield CachedQueryResult.get_by_id_async(
                    cache_key)
                do_stats = random.random() < tba_config.RECORD_FRACTION
                rpcs = []
                if cached_query is None:
                    if do_stats:
                        rpcs.append(
                            MEMCACHE_CLIENT.incr_async(random.choice(
                                self.DATABASE_MISSES_MEMCACHE_KEYS),
                                                       initial_value=0))
                    query_result = yield self._query_async()
                    if dict_version:
                        query_result = self.DICT_CONVERTER.convert(
                            query_result, dict_version)
                    if tba_config.CONFIG['database_query_cache']:
                        if dict_version:
                            rpcs.append(
                                CachedQueryResult(
                                    id=cache_key,
                                    result_dict=query_result,
                                ).put_async())
                        else:
                            rpcs.append(
                                CachedQueryResult(
                                    id=cache_key,
                                    result=query_result,
                                ).put_async())
                    updated = datetime.datetime.now()
                else:
                    if do_stats:
                        rpcs.append(
                            MEMCACHE_CLIENT.incr_async(random.choice(
                                self.DATABASE_HITS_MEMCACHE_KEYS),
                                                       initial_value=0))
                    if dict_version:
                        query_result = cached_query.result_dict
                    else:
                        query_result = cached_query.result
                    updated = cached_query.updated

                for rpc in rpcs:
                    try:
                        rpc.get_result()
                    except Exception, e:
                        logging.warning(
                            "An RPC in DatabaseQuery.fetch_async() failed!")
                if return_updated:
                    raise ndb.Return((query_result, updated))
                else:
                    raise ndb.Return(query_result)
Esempio n. 7
0
def render(template, template_values):
    from stackdriver.profiler import TraceContext
    with TraceContext() as root:
        with root.span("jinja2_engine.render({})".format(template)):
            template = JINJA_ENV.get_template(template)
            return template.render(template_values)