Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[core] only cache successful or NotFound responses
Browse files Browse the repository at this point in the history
We don't want other types of error end up in our cache, since it'll likely evict perfectly good content.
  • Loading branch information
kkaefer committed Jan 8, 2016
1 parent c5feadf commit a06f28d
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 1 deletion.
7 changes: 6 additions & 1 deletion platform/default/online_file_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,12 @@ void OnlineFileSource::Impl::startRealRequest(OnlineFileRequestImpl& request) {
auto callback = [this, &request](std::shared_ptr<const Response> response) {
request.realRequest = nullptr;

if (cache && !util::isAssetURL(request.resource.url)) {
// Only update the cache for successful or 404 responses.
// In particular, we don't want to write a Canceled request, or one that failed due to
// connection errors to the cache. Server errors are hopefully also temporary, so we're not
// caching them either.
if (cache && !util::isAssetURL(request.resource.url) &&
(!response->error || (response->error->reason == Response::Error::Reason::NotFound))) {
// Store response in database. Make sure we only refresh the expires column if the data
// didn't change.
FileCache::Hint hint = FileCache::Hint::Full;
Expand Down
172 changes: 172 additions & 0 deletions test/storage/cache_response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,175 @@ TEST_F(Storage, CacheResponse) {

loop.run();
}

// Make sure we don't store a bad server response into the cache
TEST_F(Storage, CacheNotFound) {
SCOPED_TEST(CacheNotFound);

using namespace mbgl;

util::RunLoop loop;
SQLiteCache cache(":memory:");
OnlineFileSource fs(&cache);

const Resource resource{ Resource::Unknown, "http://127.0.0.1:3000/not-found" };

// Insert existing data into the cache that will be marked as stale.
auto response = std::make_shared<Response>();
response->data = std::make_shared<const std::string>("existing data");
cache.put(resource, response, FileCache::Hint::Full);

std::unique_ptr<FileRequest> req1;
std::unique_ptr<WorkRequest> req2;

int counter = 0;

// Then, request the actual URL and check that we're getting the rigged cache response first,
// then the connection error message.
req1 = fs.request(resource, [&](Response res) {
if (counter == 0) {
EXPECT_EQ(nullptr, res.error);
EXPECT_EQ(true, res.stale);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("existing data", *res.data);
EXPECT_EQ(Seconds::zero(), res.expires);
EXPECT_EQ(Seconds::zero(), res.modified);
EXPECT_EQ("", res.etag);
} else if (counter == 1) {
EXPECT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Not Found!", *res.data);
req1.reset();

// Finally, check the cache to make sure we cached the 404 response.
req2 = cache.get(resource, [&](std::unique_ptr<Response> res2) {
EXPECT_NE(nullptr, res2->error);
EXPECT_EQ(Response::Error::Reason::NotFound, res2->error->reason);
ASSERT_TRUE(res2->data.get());
EXPECT_EQ("Not Found!", *res2->data);
CacheNotFound.finish();
loop.stop();
});
} else {
FAIL() << "Got too many responses";
}
counter++;
});

loop.run();
}

// Make sure we don't store a bad server response into the cache
TEST_F(Storage, DontCacheConnectionErrors) {
SCOPED_TEST(DontCacheConnectionErrors);

using namespace mbgl;

util::RunLoop loop;
SQLiteCache cache(":memory:");
OnlineFileSource fs(&cache);

const Resource resource{ Resource::Unknown, "http://127.0.0.1:3001" };

// Insert existing data into the cache that will be marked as stale.
auto response = std::make_shared<Response>();
response->data = std::make_shared<const std::string>("existing data");
cache.put(resource, response, FileCache::Hint::Full);

std::unique_ptr<FileRequest> req1;
std::unique_ptr<WorkRequest> req2;

int counter = 0;

// Then, request the actual URL and check that we're getting the rigged cache response first,
// then the connection error message.
req1 = fs.request(resource, [&](Response res) {
if (counter == 0) {
EXPECT_EQ(nullptr, res.error);
EXPECT_EQ(true, res.stale);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("existing data", *res.data);
EXPECT_EQ(Seconds::zero(), res.expires);
EXPECT_EQ(Seconds::zero(), res.modified);
EXPECT_EQ("", res.etag);
} else if (counter == 1) {
EXPECT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason);
req1.reset();

// Finally, check the cache to make sure we still have our original data in there rather
// than the failed connection attempt.
req2 = cache.get(resource, [&](std::unique_ptr<Response> res2) {
EXPECT_EQ(nullptr, res2->error);
ASSERT_TRUE(res2->data.get());
EXPECT_EQ("existing data", *res2->data);
DontCacheConnectionErrors.finish();
loop.stop();
});
} else {
FAIL() << "Got too many responses";
}
counter++;
});

loop.run();
}

// Make sure we don't store a bad server response into the cache
TEST_F(Storage, DontCacheServerErrors) {
SCOPED_TEST(DontCacheServerErrors);

using namespace mbgl;

util::RunLoop loop;
SQLiteCache cache(":memory:");
OnlineFileSource fs(&cache);

const Resource resource{ Resource::Unknown, "http://127.0.0.1:3000/permanent-error" };

// Insert existing data into the cache that will be marked as stale.
auto response = std::make_shared<Response>();
response->data = std::make_shared<const std::string>("existing data");
cache.put(resource, response, FileCache::Hint::Full);

std::unique_ptr<FileRequest> req1;
std::unique_ptr<WorkRequest> req2;

int counter = 0;

// Then, request the actual URL and check that we're getting the rigged cache response first,
// then the server error message.
req1 = fs.request(resource, [&](Response res) {
if (counter == 0) {
EXPECT_EQ(nullptr, res.error);
EXPECT_EQ(true, res.stale);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("existing data", *res.data);
EXPECT_EQ(Seconds::zero(), res.expires);
EXPECT_EQ(Seconds::zero(), res.modified);
EXPECT_EQ("", res.etag);
} else if (counter == 1) {
EXPECT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Server, res.error->reason);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Server Error!", *res.data);
req1.reset();

// Finally, check the cache to make sure we still have our original data in there rather
// than the failed connection attempt.
req2 = cache.get(resource, [&](std::unique_ptr<Response> res2) {
EXPECT_EQ(nullptr, res2->error);
ASSERT_TRUE(res2->data.get());
EXPECT_EQ("existing data", *res2->data);
DontCacheServerErrors.finish();
loop.stop();
});
} else {
FAIL() << "Got too many responses";
}
counter++;
});

loop.run();
}
4 changes: 4 additions & 0 deletions test/storage/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ app.get('/revalidate-etag', function(req, res) {
revalidateEtagCounter++;
});

app.get('/not-found', function(req, res) {
res.status(404).send('Not Found!');
});

app.get('/permanent-error', function(req, res) {
res.status(500).send('Server Error!');
});
Expand Down

0 comments on commit a06f28d

Please sign in to comment.