Exemple #1
0
 def format_time(self, value):
     if value is None:
         return u'<td>-</td>'
     else:
         return u'<td>%s</td>' % relative_time(value, 0, 0)
Exemple #2
0
    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=f_num(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
        age_hdr = self.parsed_hdrs.get('age', 0)
        date_hdr = self.parsed_hdrs.get('date', 0)
        if date_hdr > 0:
            apparent_age = max(0,
              int(self.res_ts - date_hdr))
        else:
            apparent_age = 0
        current_age = max(apparent_age, age_hdr)
        current_age_str = relative_time(current_age, 0, 0)        
        age_str = relative_time(age_hdr, 0, 0)
        self.age = age_hdr
        if age_hdr >= 1:
            self.setMessage('header-age header-date', rs.CURRENT_AGE,
                            age=age_str)

        # Check for clock skew and dateless origin server.
        skew = date_hdr - self.res_ts + age_hdr
        if not date_hdr:
            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)
        elif age_hdr > max_clock_skew and current_age - skew < max_clock_skew:
            self.setMessage('header-date header-age', rs.AGE_PENALTY)
        elif 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)

        # calculate freshness
        freshness_lifetime = 0
        has_explicit_freshness = False
        has_cc_freshness = False
        freshness_hdrs = ['header-date']
        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
            has_cc_freshness = True
        elif 'max-age' in cc_keys:
            freshness_lifetime = cc_dict['max-age']
            freshness_hdrs.append('header-cache-control')
            has_explicit_freshness = True
            has_cc_freshness = True
        elif self.parsed_hdrs.has_key('expires'):
            has_explicit_freshness = True
            freshness_hdrs.append('header-expires')
            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.res_ts # ?

        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
        fresh = freshness_left > 0
        if has_explicit_freshness:
            if fresh:
                self.setMessage(" ".join(freshness_hdrs), rs.FRESHNESS_FRESH,
                                 freshness_lifetime=freshness_lifetime_str,
                                 freshness_left=freshness_left_str,
                                 current_age = current_age_str
                                 )
            elif has_cc_freshness and self.age > freshness_lifetime:
                self.setMessage(" ".join(freshness_hdrs),
                                rs.FRESHNESS_STALE_CACHE,
                                freshness_lifetime=freshness_lifetime_str,
                                freshness_left=freshness_left_str,
                                current_age = current_age_str
                )
            else:
                self.setMessage(" ".join(freshness_hdrs),
                                rs.FRESHNESS_STALE_ALREADY,
                                freshness_lifetime=freshness_lifetime_str,
                                freshness_left=freshness_left_str,
                                current_age = current_age_str
                )

        # can heuristic freshness be used?
        elif self.res_status in heuristic_cacheable_status:
            self.setMessage('header-last-modified', rs.FRESHNESS_HEURISTIC)
        else:
            self.setMessage('', rs.FRESHNESS_NONE)

        # can stale responses be served?
        if 'must-revalidate' in cc_keys:
            if fresh:
                self.setMessage('header-cache-control',
                                rs.FRESH_MUST_REVALIDATE
                )
            elif has_explicit_freshness:
                self.setMessage('header-cache-control',
                                rs.STALE_MUST_REVALIDATE
                )
        elif 'proxy-revalidate' in cc_keys or 's-maxage' in cc_keys:
            if fresh:
                self.setMessage('header-cache-control',
                                rs.FRESH_PROXY_REVALIDATE
                )
            elif has_explicit_freshness:
                self.setMessage('header-cache-control',
                                rs.STALE_PROXY_REVALIDATE
                )
        else:
            if fresh:
                self.setMessage('header-cache-control', rs.FRESH_SERVABLE)
            elif has_explicit_freshness:
                self.setMessage('header-cache-control', rs.STALE_SERVABLE)

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