Example #1
0
File: red.py Project: acdha/redbot
 def checkClock(self):
     "Check for clock skew and dateless origin server."
     if not self.parsed_hdrs.has_key('date'):
         self.setMessage('', rs.DATE_CLOCKLESS)
         if self.parsed_hdrs.has_key('expires') or \
           self.parsed_hdrs.has_key('last-modified'):
             self.setMessage('header-expires header-last-modified', rs.DATE_CLOCKLESS_BAD_HDR)
         return
     skew = self.parsed_hdrs['date'] - \
       self.timestamp + self.parsed_hdrs.get('age', 0)
     if abs(skew) > max_clock_skew:
         self.setMessage('header-date', rs.DATE_INCORRECT,
                          clock_skew_string=relative_time(skew, 0, 2)
                          )
     else:
         self.setMessage('header-date', rs.DATE_CORRECT)
Example #2
0
File: red.py Project: acdha/redbot
    def checkCaching(self):
        "Examine HTTP caching characteristics."
        # TODO: check URI for query string, message about HTTP/1.0 if so
        # known Cache-Control directives that don't allow duplicates
        known_cc = ["max-age", "no-store", "s-maxage", "public", "private"
            "pre-check", "post-check", "stale-while-revalidate", "stale-if-error",
        ]

        cc_set = self.parsed_hdrs.get('cache-control', [])
        cc_list = [k for (k,v) in cc_set]
        cc_dict = dict(cc_set)
        cc_keys = cc_dict.keys()

        # check for mis-capitalised directives /
        # assure there aren't any dup directives with different values
        for cc in cc_keys:
            if cc.lower() in known_cc and cc != cc.lower():
                self.setMessage('header-cache-control', rs.CC_MISCAP,
                    cc_lower = cc.lower(), cc=cc
                )
            if cc in known_cc and cc_list.count(cc) > 1:
                self.setMessage('header-cache-control', rs.CC_DUP,
                    cc=cc
                )

        # Who can store this?
        if self.method not in cacheable_methods:
            self.store_shared = self.store_private = False
            self.setMessage('method', rs.METHOD_UNCACHEABLE, method=self.method)
            return # bail; nothing else to see here
        elif 'no-store' in cc_keys:
            self.store_shared = self.store_private = False
            self.setMessage('header-cache-control', rs.NO_STORE)
            return # bail; nothing else to see here
        elif 'private' in cc_keys:
            self.store_shared = False
            self.store_private = True
            self.setMessage('header-cache-control', rs.PRIVATE_CC)
        elif 'authorization' in [k.lower() for k, v in self.req_hdrs] and \
          not 'public' in cc_keys:
            self.store_shared = False
            self.store_private = True
            self.setMessage('header-cache-control', rs.PRIVATE_AUTH)
        else:
            self.store_shared = self.store_private = True
            self.setMessage('header-cache-control', rs.STOREABLE)

        # no-cache?
        if 'no-cache' in cc_keys:
            if "last-modified" not in self.parsed_hdrs.keys() and \
               "etag" not in self.parsed_hdrs.keys():
                self.setMessage('header-cache-control', rs.NO_CACHE_NO_VALIDATOR)
            else:
                self.setMessage('header-cache-control', rs.NO_CACHE)
            return

        # pre-check / post-check
        if 'pre-check' in cc_keys or 'post-check' in cc_keys:
            if 'pre-check' not in cc_keys or 'post_check' not in cc_keys:
                self.setMessage('header-cache-control', rs.CHECK_SINGLE)
            else:
                pre_check = post_check = None
                try:
                    pre_check = int(cc_dict['pre-check'])
                    post_check = int(cc_dict['post-check'])
                except ValueError:
                    self.setMessage('header-cache-control', rs.CHECK_NOT_INTEGER)
                if pre_check is not None and post_check is not None:
                    if pre_check == 0 and post_check == 0:
                        self.setMessage('header-cache-control', rs.CHECK_ALL_ZERO)
                    elif post_check > pre_check:
                        self.setMessage('header-cache-control', rs.CHECK_POST_BIGGER)
                        post_check = pre_check
                    elif post_check == 0:
                        self.setMessage('header-cache-control', rs.CHECK_POST_ZERO)
                    else:
                        self.setMessage('header-cache-control', rs.CHECK_POST_PRE,
                                        pre_check=pre_check, post_check=post_check)

        # vary?
        vary = self.parsed_hdrs.get('vary', set())
        if "*" in vary:
            self.setMessage('header-vary', rs.VARY_ASTERISK)
            return # bail; nothing else to see here
        elif len(vary) > 3:
            self.setMessage('header-vary', rs.VARY_COMPLEX, vary_count=len(vary))
        else:
            if "user-agent" in vary:
                self.setMessage('header-vary', rs.VARY_USER_AGENT)
            if "host" in vary:
                self.setMessage('header-vary', rs.VARY_HOST)
            # TODO: enumerate the axes in a message

        # calculate age
        if self.parsed_hdrs.has_key('date'):
            apparent_age = max(0,
              int(self.timestamp - self.parsed_hdrs['date']))
        else:
            apparent_age = 0
        age = self.parsed_hdrs.get('age', 0)
        current_age = max(apparent_age, self.parsed_hdrs.get('age', 0))
        current_age_str = relative_time(current_age, 0, 0)
        self.age = age
        if age >= 1:
            self.setMessage('header-age header-date', rs.CURRENT_AGE,
                                     current_age=current_age_str)

        # calculate freshness
        freshness_lifetime = 0
        has_explicit_freshness = False
        freshness_hdrs = ['header-date', 'header-expires']
        if 's-maxage' in cc_keys: # TODO: differentiate message for s-maxage
            freshness_lifetime = cc_dict['s-maxage']
            freshness_hdrs.append('header-cache-control')
            has_explicit_freshness = True
        elif 'max-age' in cc_keys:
            freshness_lifetime = cc_dict['max-age']
            freshness_hdrs.append('header-cache-control')
            has_explicit_freshness = True
        elif self.parsed_hdrs.has_key('expires'):
            has_explicit_freshness = True
            if self.parsed_hdrs.has_key('date'):
                freshness_lifetime = self.parsed_hdrs['expires'] - \
                    self.parsed_hdrs['date']
            else:
                freshness_lifetime = self.parsed_hdrs['expires'] - \
                    self.timestamp # ?

        freshness_left = freshness_lifetime - current_age
        freshness_left_str = relative_time(abs(int(freshness_left)), 0, 0)
        freshness_lifetime_str = relative_time(int(freshness_lifetime), 0, 0)

        self.freshness_lifetime = freshness_lifetime
        if freshness_left > 0:
            self.setMessage(" ".join(freshness_hdrs), rs.FRESHNESS_FRESH,
                             freshness_lifetime=freshness_lifetime_str,
                             freshness_left=freshness_left_str,
                             current_age = current_age_str
                             )
        else:
            self.setMessage(" ".join(freshness_hdrs), rs.FRESHNESS_STALE,
                             freshness_lifetime=freshness_lifetime_str,
                             freshness_left=freshness_left_str,
                             current_age = current_age_str
                             )

        # can heuristic freshness be used?
        if self.res_status in heuristic_cacheable_status and \
          not has_explicit_freshness:
            self.setMessage('header-last-modified', rs.HEURISTIC_FRESHNESS)

        # can stale responses be served?
        if 'must-revalidate' in cc_keys:
            self.stale_serveable = False
            self.setMessage('header-cache-control', rs.STALE_MUST_REVALIDATE)
        elif 'proxy-revalidate' in cc_keys or 's-maxage' in cc_keys:
            self.stale_serveable = False
            self.setMessage('header-cache-control', rs.STALE_PROXY_REVALIDATE)
        else:
            self.stale_serveable = True
            self.setMessage('header-cache-control', rs.STALE_SERVABLE)

        # public?
        if 'public' in cc_keys:
            self.setMessage('header-cache-control', rs.PUBLIC)