Ejemplo n.º 1
0
class TestETag(object):
    """Test our equal priority caching with ETags

    Equal Priority Caching is a term I've defined to describe when
    ETags are cached orthgonally from Time Based Caching.
    """

    @pytest.fixture()
    def sess(self, server):
        self.etag_url = urljoin(server.application_url, "/etag")
        self.update_etag_url = urljoin(server.application_url, "/update_etag")
        self.cache = DictCache()
        sess = CacheControl(requests.Session(), cache=self.cache, serializer=NullSerializer())
        return sess

    def test_etags_get_example(self, sess, server):
        """RFC 2616 14.26

        The If-None-Match request-header field is used with a method to make
        it conditional. A client that has one or more entities previously
        obtained from the resource can verify that none of those entities
        is current by including a list of their associated entity tags in
        the If-None-Match header field. The purpose of this feature is to
        allow efficient updates of cached information with a minimum amount
        of transaction overhead

        If any of the entity tags match the entity tag of the entity that
        would have been returned in the response to a similar GET request
        (without the If-None-Match header) on that resource, [...] then
        the server MUST NOT perform the requested method, [...]. Instead, if
        the request method was GET or HEAD, the server SHOULD respond with
        a 304 (Not Modified) response, including the cache-related header
        fields (particularly ETag) of one of the entities that matched.

        (Paraphrased) A server may provide an ETag header on a response. On
        subsequent queries, the client may reference the value of this Etag
        header in an If-None-Match header; on receiving such a header, the
        server can check whether the entity at that URL has changed from the
        clients last version, and if not, it can return a 304 to indicate
        the client can use it's current representation.
        """
        r = sess.get(self.etag_url)

        # make sure we cached it
        assert self.cache.get(self.etag_url) == r.raw

        # make the same request
        resp = sess.get(self.etag_url)
        assert resp.raw == r.raw
        assert resp.from_cache

        # tell the server to change the etags of the response
        sess.get(self.update_etag_url)

        resp = sess.get(self.etag_url)
        assert resp != r
        assert not resp.from_cache

        # Make sure we updated our cache with the new etag'd response.
        assert self.cache.get(self.etag_url) == resp.raw
Ejemplo n.º 2
0
class TestETag(object):
    """Test our equal priority caching with ETags

    Equal Priority Caching is a term I've defined to describe when
    ETags are cached orthgonally from Time Based Caching.
    """
    @pytest.fixture()
    def sess(self, server):
        self.etag_url = urljoin(server.application_url, '/etag')
        self.update_etag_url = urljoin(server.application_url, '/update_etag')
        self.cache = DictCache()
        sess = CacheControl(requests.Session(), cache=self.cache)
        return sess

    def test_etags_get_example(self, sess, server):
        """RFC 2616 14.26

        The If-None-Match request-header field is used with a method to make
        it conditional. A client that has one or more entities previously
        obtained from the resource can verify that none of those entities
        is current by including a list of their associated entity tags in
        the If-None-Match header field. The purpose of this feature is to
        allow efficient updates of cached information with a minimum amount
        of transaction overhead

        If any of the entity tags match the entity tag of the entity that
        would have been returned in the response to a similar GET request
        (without the If-None-Match header) on that resource, [...] then
        the server MUST NOT perform the requested method, [...]. Instead, if
        the request method was GET or HEAD, the server SHOULD respond with
        a 304 (Not Modified) response, including the cache-related header
        fields (particularly ETag) of one of the entities that matched.

        (Paraphrased) A server may provide an ETag header on a response. On
        subsequent queries, the client may reference the value of this Etag
        header in an If-None-Match header; on receiving such a header, the
        server can check whether the entity at that URL has changed from the
        clients last version, and if not, it can return a 304 to indicate
        the client can use it's current representation.
        """
        r = sess.get(self.etag_url)

        # make sure we cached it
        assert self.cache.get(self.etag_url) == r

        # make the same request
        resp = sess.get(self.etag_url)
        assert resp == r
        assert resp.from_cache

        # tell the server to change the etags of the response
        sess.get(self.update_etag_url)

        resp = sess.get(self.etag_url)
        assert resp != r
        assert not resp.from_cache

        # Make sure we updated our cache with the new etag'd response.
        assert self.cache.get(self.etag_url) == resp
Ejemplo n.º 3
0
class TestMaxAge(object):

    @pytest.fixture()
    def sess(self, url):
        self.url = url
        self.cache = DictCache()
        sess = Session()
        sess.mount(
            'http://',
            CacheControlAdapter(self.cache, serializer=NullSerializer()),
        )
        return sess

    def test_client_max_age_0(self, sess):
        """
        Making sure when the client uses max-age=0 we don't get a
        cached copy even though we're still fresh.
        """
        print('first request')
        r = sess.get(self.url)
        assert self.cache.get(self.url) == r.raw

        print('second request')
        r = sess.get(self.url, headers={'Cache-Control': 'max-age=0'})

        # don't remove from the cache
        assert self.cache.get(self.url)
        assert not r.from_cache

    def test_client_max_age_3600(self, sess):
        """
        Verify we get a cached value when the client has a
        reasonable max-age value.
        """
        r = sess.get(self.url)
        assert self.cache.get(self.url) == r.raw

        # request that we don't want a new one unless
        r = sess.get(self.url, headers={'Cache-Control': 'max-age=3600'})
        assert r.from_cache is True

        # now lets grab one that forces a new request b/c the cache
        # has expired. To do that we'll inject a new time value.
        resp = self.cache.get(self.url)
        resp.headers['date'] = 'Tue, 15 Nov 1994 08:12:31 GMT'
        r = sess.get(self.url)
        assert not r.from_cache
Ejemplo n.º 4
0
class TestVary(object):

    @pytest.fixture()
    def sess(self, server):
        self.url = urljoin(server.application_url, '/vary_accept')
        self.cache = DictCache()
        sess = CacheControl(requests.Session(), cache=self.cache)
        return sess

    def test_vary_example(self, sess):
        """RFC 2616 13.6

        When the cache receives a subsequent request whose Request-URI
        specifies one or more cache entries including a Vary header field,
        the cache MUST NOT use such a cache entry to construct a response
        to the new request unless all of the selecting request-headers
        present in the new request match the corresponding stored
        request-headers in the original request.

        Or, in simpler terms, when you make a request and the server
        returns defines a Vary header, unless all the headers listed
        in the Vary header are the same, it won't use the cached
        value.
        """
        r = sess.get(self.url)

        # make sure we cached it
        assert self.cache.get(self.url) == r

        # make the same request
        resp = sess.get(self.url)
        assert resp == r
        assert resp.from_cache

        # make a similar request, changing the accept header
        resp = sess.get(self.url, headers={'Accept': 'text/plain, text/html'})
        assert resp != r
        assert not resp.from_cache

        # Just confirming two things here:
        #
        #   1) The server used the vary header
        #   2) We have more than one header we vary on
        #
        # The reason for this is that when we don't specify the header
        # in the request, it is considered the same in terms of
        # whether or not to use the cached value.
        assert 'vary' in r.headers
        assert len(r.headers['vary'].replace(' ', '').split(',')) == 2
Ejemplo n.º 5
0
class TestChunkedResponse(object):
    @pytest.fixture()
    def sess(self, chunking_server):
        self.url = chunking_server.base_url
        self.cache = DictCache()
        sess = Session()
        sess.mount(
            'http://',
            CacheControlAdapter(self.cache, serializer=NullSerializer()),
        )
        return sess

    def test_cache_chunked_response(self, sess):
        """
        Verify that an otherwise cacheable response is cached when the response
        is chunked.
        """
        r = sess.get(self.url)
        assert self.cache.get(self.url) == r.raw

        r = sess.get(self.url, headers={'Cache-Control': 'max-age=3600'})
        assert r.from_cache is True
class TestChunkedResponse(object):

    @pytest.fixture()
    def sess(self, chunking_server):
        self.url = chunking_server.base_url
        self.cache = DictCache()
        sess = Session()
        sess.mount(
            'http://',
            CacheControlAdapter(self.cache, serializer=NullSerializer()),
        )
        return sess

    def test_cache_chunked_response(self, sess):
        """
        Verify that an otherwise cacheable response is cached when the response
        is chunked.
        """
        r = sess.get(self.url)
        assert self.cache.get(self.url) == r.raw

        r = sess.get(self.url, headers={'Cache-Control': 'max-age=3600'})
        assert r.from_cache is True
Ejemplo n.º 7
0
class TestVary(object):
    @pytest.fixture()
    def sess(self, url):
        self.url = urljoin(url, "/vary_accept")
        self.cache = DictCache()
        sess = CacheControl(requests.Session(), cache=self.cache)
        return sess

    def cached_equal(self, cached, resp):
        # remove any transfer-encoding headers as they don't apply to
        # a cached value
        if "chunked" in resp.raw.headers.get("transfer-encoding", ""):
            resp.raw.headers.pop("transfer-encoding")

        checks = [
            cached._fp.getvalue() == resp.content,
            cached.headers == resp.raw.headers,
            cached.status == resp.raw.status,
            cached.version == resp.raw.version,
            cached.reason == resp.raw.reason,
            cached.strict == resp.raw.strict,
            cached.decode_content == resp.raw.decode_content,
        ]

        print(checks)
        pprint(dict(cached.headers))
        pprint(dict(resp.raw.headers))
        return all(checks)

    def test_vary_example(self, sess):
        """RFC 2616 13.6

        When the cache receives a subsequent request whose Request-URI
        specifies one or more cache entries including a Vary header field,
        the cache MUST NOT use such a cache entry to construct a response
        to the new request unless all of the selecting request-headers
        present in the new request match the corresponding stored
        request-headers in the original request.

        Or, in simpler terms, when you make a request and the server
        returns defines a Vary header, unless all the headers listed
        in the Vary header are the same, it won't use the cached
        value.
        """
        s = sess.adapters["http://"].controller.serializer
        r = sess.get(self.url)
        c = s.loads(r.request, self.cache.get('GET+' + self.url))

        # make sure we cached it
        assert self.cached_equal(c, r)

        # make the same request
        resp = sess.get(self.url)
        assert self.cached_equal(c, resp)
        assert resp.from_cache

        # make a similar request, changing the accept header
        resp = sess.get(self.url, headers={"Accept": "text/plain, text/html"})
        assert not self.cached_equal(c, resp)
        assert not resp.from_cache

        # Just confirming two things here:
        #
        #   1) The server used the vary header
        #   2) We have more than one header we vary on
        #
        # The reason for this is that when we don't specify the header
        # in the request, it is considered the same in terms of
        # whether or not to use the cached value.
        assert "vary" in r.headers
        assert len(r.headers["vary"].replace(" ", "").split(",")) == 2
Ejemplo n.º 8
0
class TestVary(object):

    @pytest.fixture()
    def sess(self, url):
        self.url = urljoin(url, '/vary_accept')
        self.cache = DictCache()
        sess = CacheControl(requests.Session(), cache=self.cache)
        return sess

    def cached_equal(self, cached, resp):
        # remove any transfer-encoding headers as they don't apply to
        # a cached value
        if 'chunked' in resp.raw.headers.get('transfer-encoding', ''):
            resp.raw.headers.pop('transfer-encoding')

        checks = [
            cached._fp.getvalue() == resp.content,
            cached.headers == resp.raw.headers,
            cached.status == resp.raw.status,
            cached.version == resp.raw.version,
            cached.reason == resp.raw.reason,
            cached.strict == resp.raw.strict,
            cached.decode_content == resp.raw.decode_content,
        ]

        print(checks)
        pprint(dict(cached.headers))
        pprint(dict(resp.raw.headers))
        return all(checks)

    def test_vary_example(self, sess):
        """RFC 2616 13.6

        When the cache receives a subsequent request whose Request-URI
        specifies one or more cache entries including a Vary header field,
        the cache MUST NOT use such a cache entry to construct a response
        to the new request unless all of the selecting request-headers
        present in the new request match the corresponding stored
        request-headers in the original request.

        Or, in simpler terms, when you make a request and the server
        returns defines a Vary header, unless all the headers listed
        in the Vary header are the same, it won't use the cached
        value.
        """
        s = sess.adapters["http://"].controller.serializer
        r = sess.get(self.url)
        c = s.loads(r.request, self.cache.get(self.url))

        # make sure we cached it
        assert self.cached_equal(c, r)

        # make the same request
        resp = sess.get(self.url)
        assert self.cached_equal(c, resp)
        assert resp.from_cache

        # make a similar request, changing the accept header
        resp = sess.get(self.url, headers={'Accept': 'text/plain, text/html'})
        assert not self.cached_equal(c, resp)
        assert not resp.from_cache

        # Just confirming two things here:
        #
        #   1) The server used the vary header
        #   2) We have more than one header we vary on
        #
        # The reason for this is that when we don't specify the header
        # in the request, it is considered the same in terms of
        # whether or not to use the cached value.
        assert 'vary' in r.headers
        assert len(r.headers['vary'].replace(' ', '').split(',')) == 2