[core] Make response data shared to avoid excessive copying
This commit is contained in:
@@ -18,7 +18,7 @@ public:
|
||||
int64_t modified = 0;
|
||||
int64_t expires = 0;
|
||||
std::string etag;
|
||||
std::string data;
|
||||
std::shared_ptr<const std::string> data;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -221,6 +221,9 @@ void HTTPNSURLRequest::handleResult(NSData *data, NSURLResponse *res, NSError *e
|
||||
// TODO: Use different codes for host not found, timeout, invalid URL etc.
|
||||
// These can be categorized in temporary and permanent errors.
|
||||
response = std::make_unique<Response>();
|
||||
if (data) {
|
||||
response->data = std::make_shared<std::string>((const char *)[data bytes], [data length]);
|
||||
}
|
||||
response->status = Response::Error;
|
||||
response->message = [[error localizedDescription] UTF8String];
|
||||
|
||||
@@ -253,7 +256,7 @@ void HTTPNSURLRequest::handleResult(NSData *data, NSURLResponse *res, NSError *e
|
||||
const long responseCode = [(NSHTTPURLResponse *)res statusCode];
|
||||
|
||||
response = std::make_unique<Response>();
|
||||
response->data = {(const char *)[data bytes], [data length]};
|
||||
response->data = std::make_shared<std::string>((const char *)[data bytes], [data length]);
|
||||
|
||||
NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
|
||||
NSString *cache_control = [headers objectForKey:@"Cache-Control"];
|
||||
|
||||
@@ -138,8 +138,10 @@ void AssetRequest::fileStated(uv_fs_t *req) {
|
||||
#endif
|
||||
self->response->etag = util::toString(stat->st_ino);
|
||||
const auto size = (unsigned int)(stat->st_size);
|
||||
self->response->data.resize(size);
|
||||
self->buffer = uv_buf_init(const_cast<char *>(self->response->data.data()), size);
|
||||
auto data = std::make_shared<std::string>();
|
||||
self->response->data = data;
|
||||
data->resize(size);
|
||||
self->buffer = uv_buf_init(const_cast<char *>(data->data()), size);
|
||||
uv_fs_req_cleanup(req);
|
||||
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
|
||||
uv_fs_read(req->loop, req, self->fd, self->buffer.base, self->buffer.len, -1, fileRead);
|
||||
|
||||
@@ -180,8 +180,10 @@ void AssetRequest::fileStated(uv_zip_t *zip) {
|
||||
response = std::make_unique<Response>();
|
||||
|
||||
// Allocate the space for reading the data.
|
||||
response->data.resize(zip->stat->size);
|
||||
buffer = uv_buf_init(const_cast<char *>(response->data.data()), zip->stat->size);
|
||||
auto data = std::make_shared<std::string>();
|
||||
data->resize(zip->stat->size);
|
||||
buffer = uv_buf_init(const_cast<char *>(data->data()), zip->stat->size);
|
||||
response->data = data;
|
||||
|
||||
// Get the modification time in case we have one.
|
||||
if (zip->stat->valid & ZIP_STAT_MTIME) {
|
||||
|
||||
@@ -111,6 +111,7 @@ private:
|
||||
HTTPCURLContext *context = nullptr;
|
||||
|
||||
// Will store the current response.
|
||||
std::shared_ptr<std::string> data;
|
||||
std::unique_ptr<Response> response;
|
||||
|
||||
// In case of revalidation requests, this will store the old response.
|
||||
@@ -516,11 +517,11 @@ size_t HTTPCURLRequest::writeCallback(void *const contents, const size_t size, c
|
||||
auto impl = reinterpret_cast<HTTPCURLRequest *>(userp);
|
||||
MBGL_VERIFY_THREAD(impl->tid);
|
||||
|
||||
if (!impl->response) {
|
||||
impl->response = std::make_unique<Response>();
|
||||
if (!impl->data) {
|
||||
impl->data = std::make_shared<std::string>();
|
||||
}
|
||||
|
||||
impl->response->data.append((char *)contents, size * nmemb);
|
||||
impl->data->append((char *)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
@@ -688,22 +689,27 @@ void HTTPCURLRequest::handleResult(CURLcode code) {
|
||||
// This is an unsolicited 304 response and should only happen on malfunctioning
|
||||
// HTTP servers. It likely doesn't include any data, but we don't have much options.
|
||||
response->status = Response::Successful;
|
||||
response->data = std::move(data);
|
||||
return finish(ResponseStatus::Successful);
|
||||
}
|
||||
} else if (responseCode == 200) {
|
||||
response->status = Response::Successful;
|
||||
response->data = std::move(data);
|
||||
return finish(ResponseStatus::Successful);
|
||||
} else if (responseCode == 404) {
|
||||
response->status = Response::NotFound;
|
||||
response->data = std::move(data);
|
||||
return finish(ResponseStatus::Successful);
|
||||
} else if (responseCode >= 500 && responseCode < 600) {
|
||||
// Server errors may be temporary, so back off exponentially.
|
||||
response->status = Response::Error;
|
||||
response->data = std::move(data);
|
||||
response->message = "HTTP status code " + util::toString(responseCode);
|
||||
return finish(ResponseStatus::TemporaryError);
|
||||
} else {
|
||||
// We don't know how to handle any other errors, so declare them as permanently failing.
|
||||
response->status = Response::Error;
|
||||
response->data = std::move(data);
|
||||
response->message = "HTTP status code " + util::toString(responseCode);
|
||||
return finish(ResponseStatus::PermanentError);
|
||||
}
|
||||
|
||||
@@ -117,9 +117,9 @@ void SQLiteCache::Impl::get(const Resource &resource, Callback callback) {
|
||||
response->modified = getStmt->get<int64_t>(1);
|
||||
response->etag = getStmt->get<std::string>(2);
|
||||
response->expires = getStmt->get<int64_t>(3);
|
||||
response->data = getStmt->get<std::string>(4);
|
||||
response->data = std::make_shared<std::string>(std::move(getStmt->get<std::string>(4)));
|
||||
if (getStmt->get<int>(5)) { // == compressed
|
||||
response->data = util::decompress(response->data);
|
||||
response->data = std::make_shared<std::string>(std::move(util::decompress(*response->data)));
|
||||
}
|
||||
callback(std::move(response));
|
||||
} else {
|
||||
@@ -171,18 +171,21 @@ void SQLiteCache::Impl::put(const Resource& resource, std::shared_ptr<const Resp
|
||||
putStmt->bind(6 /* expires */, response->expires);
|
||||
|
||||
std::string data;
|
||||
if (resource.kind != Resource::SpriteImage) {
|
||||
if (resource.kind != Resource::SpriteImage && response->data) {
|
||||
// Do not compress images, since they are typically compressed already.
|
||||
data = util::compress(response->data);
|
||||
data = util::compress(*response->data);
|
||||
}
|
||||
|
||||
if (!data.empty() && data.size() < response->data.size()) {
|
||||
if (!data.empty() && data.size() < response->data->size()) {
|
||||
// Store the compressed data when it is smaller than the original
|
||||
// uncompressed data.
|
||||
putStmt->bind(7 /* data */, data, false); // do not retain the string internally.
|
||||
putStmt->bind(8 /* compressed */, true);
|
||||
} else if (response->data) {
|
||||
putStmt->bind(7 /* data */, *response->data, false); // do not retain the string internally.
|
||||
putStmt->bind(8 /* compressed */, false);
|
||||
} else {
|
||||
putStmt->bind(7 /* data */, response->data, false); // do not retain the string internally.
|
||||
putStmt->bind(7 /* data */, "", false);
|
||||
putStmt->bind(8 /* compressed */, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,10 +110,10 @@ NAN_METHOD(NodeRequest::Respond) {
|
||||
if (Nan::Has(res, Nan::New("data").ToLocalChecked()).FromJust()) {
|
||||
auto dataHandle = Nan::Get(res, Nan::New("data").ToLocalChecked()).ToLocalChecked();
|
||||
if (node::Buffer::HasInstance(dataHandle)) {
|
||||
response->data = std::string {
|
||||
response->data = std::make_shared<std::string>(
|
||||
node::Buffer::Data(dataHandle),
|
||||
node::Buffer::Length(dataHandle)
|
||||
};
|
||||
);
|
||||
} else {
|
||||
return Nan::ThrowTypeError("Response data must be a Buffer");
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ void MapContext::setStyleURL(const std::string& url) {
|
||||
styleRequest = nullptr;
|
||||
|
||||
if (res.status == Response::Successful) {
|
||||
loadStyleJSON(res.data, base);
|
||||
loadStyleJSON(*res.data, base);
|
||||
} else if (res.status == Response::NotFound && styleURL.find("mapbox://") == 0) {
|
||||
Log::Error(Event::Setup, "style %s could not be found or is an incompatible legacy map or style", styleURL.c_str());
|
||||
} else {
|
||||
|
||||
@@ -163,7 +163,7 @@ void Source::load() {
|
||||
}
|
||||
|
||||
rapidjson::Document d;
|
||||
d.Parse<0>(res.data.c_str());
|
||||
d.Parse<0>(res.data->c_str());
|
||||
|
||||
if (d.HasParseError()) {
|
||||
std::stringstream message;
|
||||
|
||||
@@ -20,10 +20,8 @@
|
||||
namespace mbgl {
|
||||
|
||||
struct Sprite::Loader {
|
||||
bool loadedJSON = false;
|
||||
bool loadedImage = false;
|
||||
std::unique_ptr<Data> data = std::make_unique<Data>();
|
||||
|
||||
std::shared_ptr<const std::string> image;
|
||||
std::shared_ptr<const std::string> json;
|
||||
RequestHolder jsonRequest;
|
||||
RequestHolder spriteRequest;
|
||||
};
|
||||
@@ -50,8 +48,7 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_)
|
||||
}
|
||||
loader->jsonRequest = nullptr;
|
||||
if (res.status == Response::Successful) {
|
||||
loader->data->json = res.data;
|
||||
loader->loadedJSON = true;
|
||||
loader->json = res.data;
|
||||
} else {
|
||||
std::stringstream message;
|
||||
message << "Failed to load [" << jsonURL << "]: " << res.message;
|
||||
@@ -70,8 +67,7 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_)
|
||||
}
|
||||
loader->spriteRequest = nullptr;
|
||||
if (res.status == Response::Successful) {
|
||||
loader->data->image = res.data;
|
||||
loader->loadedImage = true;
|
||||
loader->image = res.data;
|
||||
} else {
|
||||
std::stringstream message;
|
||||
message << "Failed to load [" << spriteURL << "]: " << res.message;
|
||||
@@ -88,14 +84,12 @@ Sprite::~Sprite() {
|
||||
void Sprite::emitSpriteLoadedIfComplete() {
|
||||
assert(loader);
|
||||
|
||||
if (!loader->loadedImage || !loader->loadedJSON || !observer) {
|
||||
if (!loader->image || !loader->json || !observer) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<Data> data(std::move(loader->data));
|
||||
loader.reset();
|
||||
|
||||
auto result = parseSprite(data->image, data->json);
|
||||
auto local = std::move(loader);
|
||||
auto result = parseSprite(*local->image, *local->json);
|
||||
if (result.is<Sprites>()) {
|
||||
loaded = true;
|
||||
observer->onSpriteLoaded(result.get<Sprites>());
|
||||
|
||||
@@ -19,11 +19,6 @@ class Request;
|
||||
|
||||
class Sprite : private util::noncopyable {
|
||||
public:
|
||||
struct Data {
|
||||
std::string image;
|
||||
std::string json;
|
||||
};
|
||||
|
||||
class Observer {
|
||||
public:
|
||||
virtual ~Observer() = default;
|
||||
|
||||
@@ -42,7 +42,7 @@ private:
|
||||
bool parsing = false;
|
||||
const SourceInfo& source;
|
||||
RequestHolder req;
|
||||
std::string data;
|
||||
std::shared_ptr<const std::string> data;
|
||||
float lastAngle = 0;
|
||||
float currentAngle;
|
||||
float lastPitch = 0;
|
||||
|
||||
@@ -127,7 +127,6 @@ void DefaultFileSource::Impl::update(DefaultFileRequest* request) {
|
||||
if (!request->response->stale && request->response->isExpired()) {
|
||||
// Create a new Response object with `stale = true`, but the same data, and
|
||||
// replace the current request object we have.
|
||||
// TODO: Make content shared_ptrs so we won't make copies of the content.
|
||||
auto response = std::make_shared<Response>(*request->response);
|
||||
response->stale = true;
|
||||
request->response = response;
|
||||
|
||||
@@ -98,14 +98,15 @@ GlyphPBF::GlyphPBF(GlyphStore* store,
|
||||
GlyphPBF::~GlyphPBF() = default;
|
||||
|
||||
void GlyphPBF::parse(GlyphStore* store, const std::string& fontStack, const std::string& url) {
|
||||
if (data.empty()) {
|
||||
assert(data);
|
||||
if (data->empty()) {
|
||||
// If there is no data, this means we either haven't
|
||||
// received any data.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
parseGlyphPBF(**store->getFontStack(fontStack), std::move(data));
|
||||
parseGlyphPBF(**store->getFontStack(fontStack), *data);
|
||||
} catch (const std::exception& ex) {
|
||||
std::stringstream message;
|
||||
message << "Failed to parse [" << url << "]: " << ex.what();
|
||||
|
||||
@@ -42,7 +42,7 @@ private:
|
||||
|
||||
void parse(GlyphStore* store, const std::string& fontStack, const std::string& url);
|
||||
|
||||
std::string data;
|
||||
std::shared_ptr<const std::string> data;
|
||||
std::atomic<bool> parsed;
|
||||
|
||||
RequestHolder req;
|
||||
|
||||
@@ -16,8 +16,8 @@ class Worker::Impl {
|
||||
public:
|
||||
Impl() = default;
|
||||
|
||||
void parseRasterTile(RasterBucket* bucket, std::string data, std::function<void (TileParseResult)> callback) {
|
||||
std::unique_ptr<util::Image> image(new util::Image(data));
|
||||
void parseRasterTile(RasterBucket* bucket, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
|
||||
std::unique_ptr<util::Image> image(new util::Image(*data));
|
||||
if (!(*image)) {
|
||||
callback(TileParseResult("error parsing raster image"));
|
||||
}
|
||||
@@ -29,9 +29,9 @@ public:
|
||||
callback(TileParseResult(TileData::State::parsed));
|
||||
}
|
||||
|
||||
void parseVectorTile(TileWorker* worker, std::string data, std::function<void (TileParseResult)> callback) {
|
||||
void parseVectorTile(TileWorker* worker, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
|
||||
try {
|
||||
pbf tilePBF(reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
pbf tilePBF(reinterpret_cast<const unsigned char*>(data->data()), data->size());
|
||||
callback(worker->parse(VectorTile(tilePBF)));
|
||||
} catch (const std::exception& ex) {
|
||||
callback(TileParseResult(ex.what()));
|
||||
@@ -61,12 +61,12 @@ Worker::Worker(std::size_t count) {
|
||||
|
||||
Worker::~Worker() = default;
|
||||
|
||||
std::unique_ptr<WorkRequest> Worker::parseRasterTile(RasterBucket& bucket, std::string data, std::function<void (TileParseResult)> callback) {
|
||||
std::unique_ptr<WorkRequest> Worker::parseRasterTile(RasterBucket& bucket, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
|
||||
current = (current + 1) % threads.size();
|
||||
return threads[current]->invokeWithCallback(&Worker::Impl::parseRasterTile, callback, &bucket, data);
|
||||
}
|
||||
|
||||
std::unique_ptr<WorkRequest> Worker::parseVectorTile(TileWorker& worker, std::string data, std::function<void (TileParseResult)> callback) {
|
||||
std::unique_ptr<WorkRequest> Worker::parseVectorTile(TileWorker& worker, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
|
||||
current = (current + 1) % threads.size();
|
||||
return threads[current]->invokeWithCallback(&Worker::Impl::parseVectorTile, callback, &worker, data);
|
||||
}
|
||||
|
||||
@@ -33,12 +33,12 @@ public:
|
||||
|
||||
Request parseRasterTile(
|
||||
RasterBucket&,
|
||||
std::string data,
|
||||
std::shared_ptr<const std::string> data,
|
||||
std::function<void (TileParseResult)> callback);
|
||||
|
||||
Request parseVectorTile(
|
||||
TileWorker&,
|
||||
std::string data,
|
||||
std::shared_ptr<const std::string> data,
|
||||
std::function<void (TileParseResult)> callback);
|
||||
|
||||
Request parseLiveTile(
|
||||
|
||||
7
test/fixtures/mock_file_source.cpp
vendored
7
test/fixtures/mock_file_source.cpp
vendored
@@ -55,7 +55,7 @@ void MockFileSource::Impl::replyWithSuccess(Request* req) const {
|
||||
res->status = Response::Status::Successful;
|
||||
|
||||
try {
|
||||
res->data = util::read_file(req->resource.url);
|
||||
res->data = std::make_shared<const std::string>(std::move(util::read_file(req->resource.url)));
|
||||
} catch (const std::exception& err) {
|
||||
res->status = Response::Status::Error;
|
||||
res->message = err.what();
|
||||
@@ -95,8 +95,9 @@ void MockFileSource::Impl::replyWithCorruptedData(Request* req) const {
|
||||
|
||||
std::shared_ptr<Response> res = std::make_shared<Response>();
|
||||
res->status = Response::Status::Successful;
|
||||
res->data = util::read_file(req->resource.url);
|
||||
res->data.insert(0, "CORRUPTED");
|
||||
auto data = std::make_shared<std::string>(std::move(util::read_file(req->resource.url)));
|
||||
data->insert(0, "CORRUPTED");
|
||||
res->data = std::move(data);
|
||||
|
||||
req->notify(res);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ TEST_F(Storage, CacheResponse) {
|
||||
fs.cancel(req);
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Response 1", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Response 1", *res.data);
|
||||
EXPECT_LT(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
@@ -36,7 +37,8 @@ TEST_F(Storage, CacheResponse) {
|
||||
fs.cancel(req);
|
||||
EXPECT_EQ(response.status, res.status);
|
||||
EXPECT_EQ(response.stale, res.stale);
|
||||
EXPECT_EQ(response.data, res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ(*response.data, *res.data);
|
||||
EXPECT_EQ(response.expires, res.expires);
|
||||
EXPECT_EQ(response.modified, res.modified);
|
||||
EXPECT_EQ(response.etag, res.etag);
|
||||
|
||||
@@ -13,8 +13,6 @@ TEST_F(Storage, CacheRevalidateSame) {
|
||||
SQLiteCache cache(":memory:");
|
||||
DefaultFileSource fs(&cache);
|
||||
|
||||
const Response *reference = nullptr;
|
||||
|
||||
const Resource revalidateSame { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
|
||||
Request* req1 = nullptr;
|
||||
Request* req2 = nullptr;
|
||||
@@ -27,21 +25,16 @@ TEST_F(Storage, CacheRevalidateSame) {
|
||||
}
|
||||
first = false;
|
||||
|
||||
EXPECT_EQ(nullptr, reference);
|
||||
reference = &res;
|
||||
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Response", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Response", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("snowfall", res.etag);
|
||||
EXPECT_EQ("", res.message);
|
||||
|
||||
req2 = fs.request(revalidateSame, uv_default_loop(), [&, res](const Response &res2) {
|
||||
// Make sure we get a different object than before, since this request should've been revalidated.
|
||||
EXPECT_TRUE(reference != &res2);
|
||||
|
||||
if (res2.stale) {
|
||||
// Discard stale responses, if any.
|
||||
return;
|
||||
@@ -57,7 +50,9 @@ TEST_F(Storage, CacheRevalidateSame) {
|
||||
|
||||
EXPECT_EQ(Response::Successful, res2.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Response", res2.data);
|
||||
ASSERT_TRUE(res2.data.get());
|
||||
EXPECT_EQ(res.data, res2.data);
|
||||
EXPECT_EQ("Response", *res2.data);
|
||||
// We use this to indicate that a 304 reply came back.
|
||||
EXPECT_LT(0, res2.expires);
|
||||
EXPECT_EQ(0, res2.modified);
|
||||
@@ -80,8 +75,6 @@ TEST_F(Storage, CacheRevalidateModified) {
|
||||
SQLiteCache cache(":memory:");
|
||||
DefaultFileSource fs(&cache);
|
||||
|
||||
const Response *reference = nullptr;
|
||||
|
||||
const Resource revalidateModified{ Resource::Unknown,
|
||||
"http://127.0.0.1:3000/revalidate-modified" };
|
||||
Request* req1 = nullptr;
|
||||
@@ -95,21 +88,16 @@ TEST_F(Storage, CacheRevalidateModified) {
|
||||
}
|
||||
first = false;
|
||||
|
||||
EXPECT_EQ(nullptr, reference);
|
||||
reference = &res;
|
||||
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Response", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Response", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(1420070400, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
EXPECT_EQ("", res.message);
|
||||
|
||||
req2 = fs.request(revalidateModified, uv_default_loop(), [&, res](const Response &res2) {
|
||||
// Make sure we get a different object than before, since this request should've been revalidated.
|
||||
EXPECT_TRUE(reference != &res2);
|
||||
|
||||
if (res2.stale) {
|
||||
// Discard stale responses, if any.
|
||||
return;
|
||||
@@ -124,8 +112,10 @@ TEST_F(Storage, CacheRevalidateModified) {
|
||||
req2 = nullptr;
|
||||
|
||||
EXPECT_EQ(Response::Successful, res2.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Response", res2.data);
|
||||
EXPECT_EQ(false, res2.stale);
|
||||
ASSERT_TRUE(res2.data.get());
|
||||
EXPECT_EQ("Response", *res2.data);
|
||||
EXPECT_EQ(res.data, res2.data);
|
||||
// We use this to indicate that a 304 reply came back.
|
||||
EXPECT_LT(0, res2.expires);
|
||||
EXPECT_EQ(1420070400, res2.modified);
|
||||
@@ -147,8 +137,6 @@ TEST_F(Storage, CacheRevalidateEtag) {
|
||||
SQLiteCache cache(":memory:");
|
||||
DefaultFileSource fs(&cache);
|
||||
|
||||
const Response *reference = nullptr;
|
||||
|
||||
const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" };
|
||||
Request* req1 = nullptr;
|
||||
Request* req2 = nullptr;
|
||||
@@ -161,21 +149,16 @@ TEST_F(Storage, CacheRevalidateEtag) {
|
||||
}
|
||||
first = false;
|
||||
|
||||
EXPECT_EQ(nullptr, reference);
|
||||
reference = &res;
|
||||
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Response 1", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Response 1", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("response-1", res.etag);
|
||||
EXPECT_EQ("", res.message);
|
||||
|
||||
req2 = fs.request(revalidateEtag, uv_default_loop(), [&, res](const Response &res2) {
|
||||
// Make sure we get a different object than before, since this request should've been revalidated.
|
||||
EXPECT_TRUE(reference != &res2);
|
||||
|
||||
if (res2.stale) {
|
||||
// Discard stale responses, if any.
|
||||
return;
|
||||
@@ -191,7 +174,9 @@ TEST_F(Storage, CacheRevalidateEtag) {
|
||||
|
||||
EXPECT_EQ(Response::Successful, res2.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Response 2", res2.data);
|
||||
ASSERT_TRUE(res2.data.get());
|
||||
EXPECT_NE(res.data, res2.data);
|
||||
EXPECT_EQ("Response 2", *res2.data);
|
||||
EXPECT_EQ(0, res2.expires);
|
||||
EXPECT_EQ(0, res2.modified);
|
||||
EXPECT_EQ("response-2", res2.etag);
|
||||
|
||||
@@ -178,11 +178,12 @@ TEST_F(Storage, DatabaseLockedWrite) {
|
||||
Log::setObserver(std::make_unique<FixtureLogObserver>());
|
||||
|
||||
auto response = std::make_shared<Response>();
|
||||
response->data = "Demo";
|
||||
response->data = std::make_shared<std::string>("Demo");
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) {
|
||||
ASSERT_NE(nullptr, res.get());
|
||||
EXPECT_EQ("Demo", res->data);
|
||||
ASSERT_TRUE(res->data.get());
|
||||
EXPECT_EQ("Demo", *res->data);
|
||||
});
|
||||
|
||||
// Make sure that we got a no errors
|
||||
@@ -210,7 +211,7 @@ TEST_F(Storage, DatabaseLockedRefresh) {
|
||||
Log::setObserver(std::make_unique<FixtureLogObserver>());
|
||||
|
||||
auto response = std::make_shared<Response>();
|
||||
response->data = "Demo";
|
||||
response->data = std::make_shared<std::string>("Demo");
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) {
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
@@ -226,7 +227,7 @@ TEST_F(Storage, DatabaseLockedRefresh) {
|
||||
Log::setObserver(std::make_unique<FixtureLogObserver>());
|
||||
|
||||
auto response = std::make_shared<Response>();
|
||||
response->data = "Demo";
|
||||
response->data = std::make_shared<std::string>("Demo");
|
||||
cache.refresh({ Resource::Unknown, "mapbox://test" }, response->expires);
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) {
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
@@ -255,11 +256,12 @@ TEST_F(Storage, DatabaseDeleted) {
|
||||
Log::setObserver(std::make_unique<FixtureLogObserver>());
|
||||
|
||||
auto response = std::make_shared<Response>();
|
||||
response->data = "Demo";
|
||||
response->data = std::make_shared<std::string>("Demo");
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) {
|
||||
ASSERT_NE(nullptr, res.get());
|
||||
EXPECT_EQ("Demo", res->data);
|
||||
ASSERT_TRUE(res->data.get());
|
||||
EXPECT_EQ("Demo", *res->data);
|
||||
});
|
||||
|
||||
Log::removeObserver();
|
||||
@@ -272,11 +274,12 @@ TEST_F(Storage, DatabaseDeleted) {
|
||||
Log::setObserver(std::make_unique<FixtureLogObserver>());
|
||||
|
||||
auto response = std::make_shared<Response>();
|
||||
response->data = "Demo";
|
||||
response->data = std::make_shared<std::string>("Demo");
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) {
|
||||
ASSERT_NE(nullptr, res.get());
|
||||
EXPECT_EQ("Demo", res->data);
|
||||
ASSERT_TRUE(res->data.get());
|
||||
EXPECT_EQ("Demo", *res->data);
|
||||
});
|
||||
|
||||
auto observer = Log::removeObserver();
|
||||
@@ -302,11 +305,12 @@ TEST_F(Storage, DatabaseInvalid) {
|
||||
Log::setObserver(std::make_unique<FixtureLogObserver>());
|
||||
|
||||
auto response = std::make_shared<Response>();
|
||||
response->data = "Demo";
|
||||
response->data = std::make_shared<std::string>("Demo");
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) {
|
||||
ASSERT_NE(nullptr, res.get());
|
||||
EXPECT_EQ("Demo", res->data);
|
||||
ASSERT_TRUE(res->data.get());
|
||||
EXPECT_EQ("Demo", *res->data);
|
||||
});
|
||||
|
||||
auto observer = Log::removeObserver();
|
||||
|
||||
@@ -20,7 +20,7 @@ TEST_F(Storage, AssetReadDirectory) {
|
||||
fs.cancel(req);
|
||||
EXPECT_EQ(Response::Error, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ(0ul, res.data.size());
|
||||
ASSERT_FALSE(res.data.get());
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
|
||||
@@ -21,7 +21,8 @@ TEST_F(Storage, AssetEmptyFile) {
|
||||
fs.cancel(req);
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ(0ul, res.data.size());
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_LT(1420000000, res.modified);
|
||||
EXPECT_NE("", res.etag);
|
||||
@@ -48,12 +49,14 @@ TEST_F(Storage, AssetNonEmptyFile) {
|
||||
fs.cancel(req);
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ(16ul, res.data.size());
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("content is here\n", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_LT(1420000000, res.modified);
|
||||
EXPECT_NE("", res.etag);
|
||||
EXPECT_EQ("", res.message);
|
||||
EXPECT_EQ("content is here\n", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("content is here\n", *res.data);
|
||||
NonEmptyFile.finish();
|
||||
});
|
||||
|
||||
@@ -76,7 +79,7 @@ TEST_F(Storage, AssetNonExistentFile) {
|
||||
fs.cancel(req);
|
||||
EXPECT_EQ(Response::Error, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ(0ul, res.data.size());
|
||||
ASSERT_FALSE(res.data.get());
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
|
||||
@@ -40,7 +40,8 @@ TEST_F(Storage, HTTPCancelMultiple) {
|
||||
fs.cancel(req);
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
|
||||
@@ -27,7 +27,8 @@ TEST_F(Storage, HTTPCoalescing) {
|
||||
}
|
||||
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
@@ -69,7 +70,8 @@ TEST_F(Storage, HTTPMultiple) {
|
||||
|
||||
// Do not cancel the request right away.
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_EQ(2147483647, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
@@ -85,7 +87,8 @@ TEST_F(Storage, HTTPMultiple) {
|
||||
fs.cancel(req2);
|
||||
|
||||
EXPECT_EQ(Response::Successful, res2.status);
|
||||
EXPECT_EQ("Hello World!", res2.data);
|
||||
ASSERT_TRUE(res2.data.get());
|
||||
EXPECT_EQ("Hello World!", *res2.data);
|
||||
EXPECT_EQ(2147483647, res2.expires);
|
||||
EXPECT_EQ(0, res2.modified);
|
||||
EXPECT_EQ("", res2.etag);
|
||||
@@ -115,7 +118,8 @@ TEST_F(Storage, HTTPStale) {
|
||||
req1 = fs.request(resource, uv_default_loop(), [&] (const Response &res) {
|
||||
// Do not cancel the request right away.
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
@@ -132,7 +136,8 @@ TEST_F(Storage, HTTPStale) {
|
||||
// Start a second request for the same resource after the first one has been completed.
|
||||
req2 = fs.request(resource, uv_default_loop(), [&] (const Response &res2) {
|
||||
EXPECT_EQ(Response::Successful, res2.status);
|
||||
EXPECT_EQ("Hello World!", res2.data);
|
||||
ASSERT_TRUE(res2.data.get());
|
||||
EXPECT_EQ("Hello World!", *res2.data);
|
||||
EXPECT_EQ(0, res2.expires);
|
||||
EXPECT_EQ(0, res2.modified);
|
||||
EXPECT_EQ("", res2.etag);
|
||||
|
||||
@@ -38,7 +38,8 @@ TEST_F(Storage, HTTPError) {
|
||||
EXPECT_GT(1.2, duration) << "Backoff timer fired too late";
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
@@ -67,7 +68,7 @@ TEST_F(Storage, HTTPError) {
|
||||
#else
|
||||
FAIL();
|
||||
#endif
|
||||
EXPECT_EQ("", res.data);
|
||||
ASSERT_FALSE(res.data.get());
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
|
||||
@@ -21,7 +21,8 @@ TEST_F(Storage, HTTPHeaderParsing) {
|
||||
fs.cancel(req1);
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_EQ(1420797926, res.expires);
|
||||
EXPECT_EQ(1420794326, res.modified);
|
||||
EXPECT_EQ("foo", res.etag);
|
||||
@@ -37,7 +38,8 @@ TEST_F(Storage, HTTPHeaderParsing) {
|
||||
fs.cancel(req2);
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_GT(2, std::abs(res.expires - now - 120)) << "Expiration date isn't about 120 seconds in the future";
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
|
||||
@@ -34,7 +34,8 @@ TEST_F(Storage, HTTPIssue1369) {
|
||||
fs.cancel(req);
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
|
||||
@@ -26,7 +26,8 @@ TEST_F(Storage, HTTPLoad) {
|
||||
reqs[i] = nullptr;
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ(std::string("Request ") + std::to_string(current), res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
|
||||
@@ -17,7 +17,8 @@ TEST_F(Storage, HTTPOtherLoop) {
|
||||
fs.cancel(req);
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
|
||||
@@ -24,7 +24,8 @@ TEST_F(Storage, HTTPReading) {
|
||||
EXPECT_EQ(uv_thread_self(), mainThread);
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
@@ -38,6 +39,8 @@ TEST_F(Storage, HTTPReading) {
|
||||
EXPECT_EQ(uv_thread_self(), mainThread);
|
||||
EXPECT_EQ(Response::NotFound, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Cannot GET /doesnotexist\n", *res.data);
|
||||
EXPECT_EQ("", res.message);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
@@ -51,6 +54,8 @@ TEST_F(Storage, HTTPReading) {
|
||||
EXPECT_EQ(uv_thread_self(), mainThread);
|
||||
EXPECT_EQ(Response::Error, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Server Error!", *res.data);
|
||||
EXPECT_EQ("HTTP status code 500", res.message);
|
||||
EXPECT_EQ(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
|
||||
@@ -20,7 +20,8 @@ TEST_F(Storage, HTTPTimeout) {
|
||||
counter++;
|
||||
EXPECT_EQ(Response::Successful, res.status);
|
||||
EXPECT_EQ(false, res.stale);
|
||||
EXPECT_EQ("Hello World!", res.data);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("Hello World!", *res.data);
|
||||
EXPECT_LT(0, res.expires);
|
||||
EXPECT_EQ(0, res.modified);
|
||||
EXPECT_EQ("", res.etag);
|
||||
|
||||
@@ -74,7 +74,7 @@ app.get('/revalidate-etag', function(req, res) {
|
||||
});
|
||||
|
||||
app.get('/permanent-error', function(req, res) {
|
||||
res.status(500).end();
|
||||
res.status(500).send('Server Error!');
|
||||
});
|
||||
|
||||
var temporaryErrorCounter = 0;
|
||||
|
||||
Reference in New Issue
Block a user