summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2015-03-04 12:32:14 +0100
committerKonstantin Käfer <mail@kkaefer.com>2015-03-06 08:21:47 -0800
commit9781785ab73e8394e8b92625cc4741952f47955d (patch)
tree1b9e6b86c1c9ca19bb1bf1c52bcd0e41410ced66
parent8c0acecbe362be4a40638491b67ee5fe3d23a65e (diff)
downloadqtlocation-mapboxgl-9781785ab73e8394e8b92625cc4741952f47955d.tar.gz
scope Requests to an Environment object for easier cancelation
we are now scoping all file requests to an environment object. The FileSource implementation treats this as an opaque pointer, but allows canceling all Requests that are associated with that pointer. This is necessary to abort all file requests that originated from a particular Map object. Aborting a file request is different from canceling a file request: A canceled request doesn't have its callback called, while an aborted request will have its callback called with an error, indicating that the environment is going to be shut down.
-rw-r--r--.clang-format2
-rw-r--r--gyp/http-curl.gypi43
-rw-r--r--gyp/http-nsurl.gypi10
-rw-r--r--include/mbgl/map/map.hpp14
-rw-r--r--include/mbgl/storage/default/request.hpp8
-rw-r--r--include/mbgl/storage/default/shared_request_base.hpp35
-rw-r--r--include/mbgl/storage/default_file_source.hpp17
-rw-r--r--include/mbgl/storage/file_source.hpp12
-rw-r--r--platform/darwin/http_request_nsurl.mm3
-rw-r--r--src/mbgl/map/environment.cpp39
-rw-r--r--src/mbgl/map/environment.hpp44
-rw-r--r--src/mbgl/map/map.cpp40
-rw-r--r--src/mbgl/map/raster_tile_data.cpp7
-rw-r--r--src/mbgl/map/raster_tile_data.hpp2
-rw-r--r--src/mbgl/map/source.cpp47
-rw-r--r--src/mbgl/map/source.hpp22
-rw-r--r--src/mbgl/map/sprite.cpp22
-rw-r--r--src/mbgl/map/sprite.hpp7
-rw-r--r--src/mbgl/map/tile_data.cpp15
-rw-r--r--src/mbgl/map/tile_data.hpp8
-rw-r--r--src/mbgl/map/vector_tile_data.cpp4
-rw-r--r--src/mbgl/map/vector_tile_data.hpp7
-rw-r--r--src/mbgl/storage/default_file_source.cpp62
-rw-r--r--src/mbgl/storage/request.cpp4
-rw-r--r--src/mbgl/text/glyph_store.cpp15
-rw-r--r--src/mbgl/text/glyph_store.hpp10
-rw-r--r--test/storage/cache_response.cpp5
-rw-r--r--test/storage/cache_revalidate.cpp17
-rw-r--r--test/storage/directory_reading.cpp7
-rw-r--r--test/storage/file_reading.cpp17
-rw-r--r--test/storage/http_cancel.cpp14
-rw-r--r--test/storage/http_coalescing.cpp4
-rw-r--r--test/storage/http_environment.cpp54
-rw-r--r--test/storage/http_error.cpp18
-rw-r--r--test/storage/http_header_parsing.cpp10
-rw-r--r--test/storage/http_load.cpp6
-rw-r--r--test/storage/http_noloop.cpp5
-rw-r--r--test/storage/http_other_loop.cpp5
-rw-r--r--test/storage/http_reading.cpp17
-rwxr-xr-xtest/storage/server.js7
-rw-r--r--test/test.gyp1
41 files changed, 485 insertions, 201 deletions
diff --git a/.clang-format b/.clang-format
index 86d562e74e..109b562b59 100644
--- a/.clang-format
+++ b/.clang-format
@@ -2,7 +2,7 @@ Standard: Cpp11
IndentWidth: 4
AccessModifierOffset: -4
UseTab: Never
-BinPackParameters: false
+BinPackParameters: true
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortBlocksOnASingleLine: false
diff --git a/gyp/http-curl.gypi b/gyp/http-curl.gypi
index bfb2054d55..c97ad370b5 100644
--- a/gyp/http-curl.gypi
+++ b/gyp/http-curl.gypi
@@ -14,6 +14,25 @@
'../include',
],
+ 'variables': {
+ 'cflags_cc': [
+ '<@(uv_cflags)',
+ '<@(curl_cflags)',
+ '<@(boost_cflags)',
+ ],
+ 'ldflags': [
+ '<@(uv_ldflags)',
+ '<@(curl_ldflags)',
+ ],
+ 'libraries': [
+ '<@(uv_static_libs)',
+ '<@(curl_static_libs)',
+ ],
+ 'defines': [
+ '-DMBGL_HTTP_CURL'
+ ],
+ },
+
'conditions': [
['host == "android"', {
'variables': {
@@ -32,19 +51,17 @@
}],
],
- 'variables': {
- 'cflags_cc': [
- '<@(uv_cflags)',
- '<@(curl_cflags)',
- '<@(boost_cflags)',
- ],
- 'ldflags': [
- '<@(uv_ldflags)',
- '<@(curl_ldflags)',
- ],
- 'libraries': [
- '<@(uv_static_libs)',
- '<@(curl_static_libs)',
+ 'direct_dependent_settings': {
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [ '<@(defines)' ],
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ],
+ }
+ }, {
+ 'cflags': [ '<@(defines)' ],
+ 'cflags_cc': [ '<@(defines)' ],
+ }]
],
},
diff --git a/gyp/http-nsurl.gypi b/gyp/http-nsurl.gypi
index 4205f59d81..5a079fdeeb 100644
--- a/gyp/http-nsurl.gypi
+++ b/gyp/http-nsurl.gypi
@@ -25,6 +25,9 @@
'libraries': [
'<@(uv_static_libs)',
],
+ 'defines': [
+ '-DMBGL_HTTP_NSURL'
+ ],
},
'xcode_settings': {
@@ -32,6 +35,13 @@
'CLANG_ENABLE_OBJC_ARC': 'NO',
},
+ 'direct_dependent_settings': {
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [ '<@(defines)' ],
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ],
+ }
+ },
+
'link_settings': {
'libraries': [ '<@(libraries)' ],
'xcode_settings': {
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 8711aa3d08..00719aa382 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -36,11 +36,7 @@ class View;
class GlyphAtlas;
class SpriteAtlas;
class LineAtlas;
-
-struct exception : std::runtime_error {
- inline exception(const char *msg) : std::runtime_error(msg) {
- }
-};
+class Environment;
class Map : private util::noncopyable {
friend class View;
@@ -182,8 +178,8 @@ private:
Mode mode = Mode::None;
-public: // TODO: make private again
- std::unique_ptr<uv::loop> loop;
+ const std::unique_ptr<Environment> env;
+ View &view;
private:
std::unique_ptr<uv::worker> workers;
@@ -214,12 +210,8 @@ private:
// Stores whether the map thread has been stopped already.
std::atomic_bool isStopped;
- View &view;
-
-#ifdef DEBUG
const std::thread::id mainThread;
std::thread::id mapThread;
-#endif
Transform transform;
TransformState state;
diff --git a/include/mbgl/storage/default/request.hpp b/include/mbgl/storage/default/request.hpp
index 648585f304..b686d1fe90 100644
--- a/include/mbgl/storage/default/request.hpp
+++ b/include/mbgl/storage/default/request.hpp
@@ -15,13 +15,14 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Response;
+class Environment;
class Request : private util::noncopyable {
MBGL_STORE_THREAD(tid)
public:
using Callback = std::function<void(const Response &)>;
- Request(const Resource &resource, uv_loop_t *loop, Callback callback);
+ Request(const Resource &resource, uv_loop_t *loop, const Environment &env, Callback callback);
public:
// May be called from any thread.
@@ -45,6 +46,11 @@ private:
public:
const Resource resource;
+
+ // The environment ref is used to associate requests with a particular environment. This allows
+ // us to only terminate requests associated with that environment, e.g. when the map the env
+ // belongs to is discarded.
+ const Environment &env;
};
}
diff --git a/include/mbgl/storage/default/shared_request_base.hpp b/include/mbgl/storage/default/shared_request_base.hpp
index 2d56615608..59e38efc2f 100644
--- a/include/mbgl/storage/default/shared_request_base.hpp
+++ b/include/mbgl/storage/default/shared_request_base.hpp
@@ -4,11 +4,13 @@
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/file_cache.hpp>
#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/default/request.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <string>
#include <set>
+#include <vector>
#include <cassert>
typedef struct uv_loop_s uv_loop_t;
@@ -45,20 +47,12 @@ public:
observers.insert(request);
}
- void unsubscribeAll() {
- MBGL_VERIFY_THREAD(tid);
-
- source = nullptr;
- observers.clear();
- cancel();
- }
-
void unsubscribe(Request *request) {
MBGL_VERIFY_THREAD(tid);
observers.erase(request);
- if (observers.empty()) {
+ if (abandoned()) {
// There are no observers anymore. We are initiating cancelation.
if (source) {
// First, remove this SharedRequestBase from the source.
@@ -70,6 +64,29 @@ public:
}
}
+ bool abandoned() const {
+ return observers.empty();
+ }
+
+ std::vector<Request *> removeAllInEnvironment(const Environment &env) {
+ MBGL_VERIFY_THREAD(tid);
+
+ std::vector<Request *> result;
+
+ // Removes all Requests in the supplied environment and returns a list
+ // of them.
+ util::erase_if(observers, [&](Request *req) -> bool {
+ if (&req->env == &env) {
+ result.push_back(req);
+ return true;
+ } else {
+ return false;
+ }
+ });
+
+ return result;
+ }
+
protected:
virtual ~SharedRequestBase() {
MBGL_VERIFY_THREAD(tid);
diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp
index 86e2414041..14b4db6eff 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -20,11 +20,14 @@ class DefaultFileSource : public FileSource {
public:
DefaultFileSource(FileCache *cache, const std::string &root = "");
DefaultFileSource(FileCache *cache, uv_loop_t *loop, const std::string &root = "");
- ~DefaultFileSource();
+ ~DefaultFileSource() override;
- Request *request(const Resource &resource, uv_loop_t *loop, Callback callback);
- void cancel(Request *request);
- void request(const Resource &resource, Callback callback);
+ Request *request(const Resource &resource, uv_loop_t *loop, const Environment &env,
+ Callback callback) override;
+ void cancel(Request *request) override;
+ void request(const Resource &resource, const Environment &env, Callback callback) override;
+
+ void abort(const Environment &env) override;
enum class CacheHint : uint8_t { Full, Refresh, No };
void notify(SharedRequestBase *sharedRequest, const std::set<Request *> &observers,
@@ -39,14 +42,16 @@ private:
struct RemoveRequestAction;
struct ResultAction;
struct StopAction;
- using Action =
- mapbox::util::variant<AddRequestAction, RemoveRequestAction, ResultAction, StopAction>;
+ struct AbortAction;
+ using Action = mapbox::util::variant<AddRequestAction, RemoveRequestAction, ResultAction,
+ StopAction, AbortAction>;
using Queue = util::AsyncQueue<Action>;
void process(AddRequestAction &action);
void process(RemoveRequestAction &action);
void process(ResultAction &action);
void process(StopAction &action);
+ void process(AbortAction &action);
SharedRequestBase *find(const Resource &resource);
diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp
index 8517d6e4a6..30e88c39f6 100644
--- a/include/mbgl/storage/file_source.hpp
+++ b/include/mbgl/storage/file_source.hpp
@@ -15,6 +15,7 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Request;
+class Environment;
class FileSource : private util::noncopyable {
protected:
@@ -27,12 +28,19 @@ public:
// These can be called from any thread. The callback will be invoked in the loop.
// You can only cancel a request from the same thread it was created in.
- virtual Request *request(const Resource &resource, uv_loop_t *loop, Callback callback) = 0;
+ virtual Request *request(const Resource &resource, uv_loop_t *loop, const Environment &env,
+ Callback callback) = 0;
virtual void cancel(Request *request) = 0;
// These can be called from any thread. The callback will be invoked in an arbitrary other thread.
// You cannot cancel these requests.
- virtual void request(const Resource &resource, Callback callback) = 0;
+ virtual void request(const Resource &resource, const Environment &env, Callback callback) = 0;
+
+ // This can be called from any thread. All requests with the environment pointer env should be
+ // notified as errored. Note that this is /different/ from canceling requests; a canceled
+ // request's callback is never called, while an aborted request's callback is called with
+ // a error message.
+ virtual void abort(const Environment &env) = 0;
};
}
diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm
index 83c010f8b8..638b5062d4 100644
--- a/platform/darwin/http_request_nsurl.mm
+++ b/platform/darwin/http_request_nsurl.mm
@@ -188,6 +188,7 @@ void HTTPRequestImpl::handleResponse() {
}
context->removeRequest(request);
+ request->ptr = nullptr;
delete request;
request = nullptr;
}
@@ -206,6 +207,8 @@ void HTTPRequestImpl::cancel() {
[task cancel];
[task release];
task = nullptr;
+ } else {
+ delete this;
}
}
diff --git a/src/mbgl/map/environment.cpp b/src/mbgl/map/environment.cpp
new file mode 100644
index 0000000000..ee13d33ea7
--- /dev/null
+++ b/src/mbgl/map/environment.cpp
@@ -0,0 +1,39 @@
+#include <mbgl/map/environment.hpp>
+#include <mbgl/storage/file_source.hpp>
+
+#include <uv.h>
+
+#include <cassert>
+
+namespace mbgl {
+
+Environment::Environment(FileSource &fs) : fileSource(fs), loop(uv_loop_new()) {
+}
+
+void Environment::setup() {
+ mapThread = std::this_thread::get_id();
+}
+
+bool Environment::inMapThread() const {
+ return std::this_thread::get_id() == mapThread;
+}
+
+void Environment::requestAsync(const Resource &resource, std::function<void(const Response &)> callback) {
+ fileSource.request(resource, *this, std::move(callback));
+}
+
+Request *Environment::request(const Resource &resource, std::function<void(const Response &)> callback) {
+ assert(inMapThread());
+ return fileSource.request(resource, loop, *this, std::move(callback));
+}
+
+void Environment::cancelRequest(Request *req) {
+ assert(inMapThread());
+ fileSource.cancel(req);
+}
+
+void Environment::terminate() {
+ fileSource.abort(*this);
+}
+
+}
diff --git a/src/mbgl/map/environment.hpp b/src/mbgl/map/environment.hpp
new file mode 100644
index 0000000000..b68a7b9e2f
--- /dev/null
+++ b/src/mbgl/map/environment.hpp
@@ -0,0 +1,44 @@
+#ifndef MBGL_MAP_MAP_ENVIRONMENT
+#define MBGL_MAP_MAP_ENVIRONMENT
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/util.hpp>
+
+#include <thread>
+#include <functional>
+
+typedef struct uv_loop_s uv_loop_t;
+
+namespace mbgl {
+
+class FileSource;
+class Request;
+class Response;
+struct Resource;
+
+class Environment : private util::noncopyable {
+public:
+ Environment(FileSource &);
+
+ void setup();
+
+ bool inMapThread() const;
+
+ void requestAsync(const Resource &, std::function<void(const Response &)>);
+ Request *request(const Resource &, std::function<void(const Response &)>);
+ void cancelRequest(Request *);
+
+ // Request to terminate the environment.
+ void terminate();
+
+private:
+ FileSource &fileSource;
+ std::thread::id mapThread;
+
+public:
+ uv_loop_t *const loop;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 518bc5bb96..0cd7d3621e 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -1,4 +1,5 @@
#include <mbgl/map/map.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/map/source.hpp>
@@ -25,6 +26,7 @@
#include <mbgl/util/string.hpp>
#include <mbgl/util/uv.hpp>
#include <mbgl/util/mapbox.hpp>
+#include <mbgl/util/exception.hpp>
#include <algorithm>
#include <iostream>
@@ -57,16 +59,14 @@ const static bool uvVersionCheck = []() {
using namespace mbgl;
Map::Map(View& view_, FileSource& fileSource_)
- : loop(util::make_unique<uv::loop>()),
+ : env(util::make_unique<Environment>(fileSource_)),
view(view_),
-#ifdef DEBUG
mainThread(std::this_thread::get_id()),
mapThread(mainThread),
-#endif
transform(view_),
fileSource(fileSource_),
glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
- glyphStore(std::make_shared<GlyphStore>(fileSource)),
+ glyphStore(std::make_shared<GlyphStore>(*env)),
spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
lineAtlas(util::make_unique<LineAtlas>(512, 512)),
texturePool(std::make_shared<TexturePool>()),
@@ -92,7 +92,7 @@ Map::~Map() {
texturePool.reset();
workers.reset();
- uv_run(**loop, UV_RUN_DEFAULT);
+ uv_run(env->loop, UV_RUN_DEFAULT);
}
uv::worker &Map::getWorker() {
@@ -112,7 +112,7 @@ void Map::start(bool startPaused) {
isStopped = false;
// Setup async notifications
- asyncTerminate = util::make_unique<uv::async>(**loop, [this]() {
+ asyncTerminate = util::make_unique<uv::async>(env->loop, [this]() {
assert(std::this_thread::get_id() == mapThread);
// Remove all of these to make sure they are destructed in the correct thread.
@@ -127,7 +127,7 @@ void Map::start(bool startPaused) {
asyncTerminate.reset();
});
- asyncRender = util::make_unique<uv::async>(**loop, [this]() {
+ asyncRender = util::make_unique<uv::async>(env->loop, [this]() {
assert(std::this_thread::get_id() == mapThread);
if (state.hasSize()) {
@@ -207,7 +207,7 @@ void Map::pause(bool waitForPause) {
pausing = true;
mutexRun.unlock();
- uv_stop(**loop);
+ uv_stop(env->loop);
rerender(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
if (waitForPause) {
@@ -242,12 +242,14 @@ void Map::run() {
}
if (mode == Mode::Static && !style && styleURL.empty()) {
- throw exception("Style is not set");
+ throw util::Exception("Style is not set");
}
view.activate();
- workers = util::make_unique<uv::worker>(**loop, 4, "Tile Worker");
+ workers = util::make_unique<uv::worker>(env->loop, 4, "Tile Worker");
+
+ env->setup();
setup();
prepare();
@@ -255,15 +257,15 @@ void Map::run() {
if (mode == Mode::Continuous) {
terminating = false;
while(!terminating) {
- uv_run(**loop, UV_RUN_DEFAULT);
+ uv_run(env->loop, UV_RUN_DEFAULT);
checkForPause();
}
} else {
- uv_run(**loop, UV_RUN_DEFAULT);
+ uv_run(env->loop, UV_RUN_DEFAULT);
}
// Run the event loop once more to make sure our async delete handlers are called.
- uv_run(**loop, UV_RUN_ONCE);
+ uv_run(env->loop, UV_RUN_ONCE);
// If the map rendering wasn't started asynchronously, we perform one render
// *after* all events have been processed.
@@ -376,7 +378,7 @@ util::ptr<Sprite> Map::getSprite() {
const float pixelRatio = state.getPixelRatio();
const std::string &sprite_url = style->getSpriteURL();
if (!sprite || sprite->pixelRatio != pixelRatio) {
- sprite = Sprite::Create(sprite_url, pixelRatio, fileSource);
+ sprite = Sprite::Create(sprite_url, pixelRatio, *env);
}
return sprite;
@@ -621,7 +623,7 @@ void Map::updateSources() {
if (source->enabled) {
if (!source->source) {
source->source = std::make_shared<Source>(source->info);
- source->source->load(*this, fileSource);
+ source->source->load(*this, *env);
}
} else {
source->source.reset();
@@ -648,10 +650,8 @@ void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
void Map::updateTiles() {
for (const auto& source : activeSources) {
- source->source->update(*this, getWorker(),
- style, *glyphAtlas, *glyphStore,
- *spriteAtlas, getSprite(),
- *texturePool, fileSource, ***loop, [this](){ update(); });
+ source->source->update(*this, *env, getWorker(), style, *glyphAtlas, *glyphStore,
+ *spriteAtlas, getSprite(), *texturePool, [this]() { update(); });
}
}
@@ -659,7 +659,7 @@ void Map::prepare() {
if (!style) {
style = std::make_shared<Style>();
- fileSource.request({ Resource::Kind::JSON, styleURL}, **loop, [&](const Response &res) {
+ env->request({ Resource::Kind::JSON, styleURL}, [&](const Response &res) {
if (res.status == Response::Successful) {
// Calculate the base
const size_t pos = styleURL.rfind('/');
diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp
index 1ca9ef8041..4cd7fc2b5e 100644
--- a/src/mbgl/map/raster_tile_data.cpp
+++ b/src/mbgl/map/raster_tile_data.cpp
@@ -4,10 +4,9 @@
using namespace mbgl;
-
-RasterTileData::RasterTileData(Tile::ID const& id_, TexturePool& texturePool, const SourceInfo& source_, FileSource& fileSource_)
- : TileData(id_, source_, fileSource_),
- bucket(texturePool, layout) {
+RasterTileData::RasterTileData(Tile::ID const &id_, TexturePool &texturePool,
+ const SourceInfo &source_, Environment &env_)
+ : TileData(id_, source_, env_), bucket(texturePool, layout) {
}
RasterTileData::~RasterTileData() {
diff --git a/src/mbgl/map/raster_tile_data.hpp b/src/mbgl/map/raster_tile_data.hpp
index 9a8578a61e..2413e13fb0 100644
--- a/src/mbgl/map/raster_tile_data.hpp
+++ b/src/mbgl/map/raster_tile_data.hpp
@@ -17,7 +17,7 @@ class RasterTileData : public TileData {
friend class TileParser;
public:
- RasterTileData(Tile::ID const& id, TexturePool&, const SourceInfo&, FileSource &);
+ RasterTileData(Tile::ID const &id, TexturePool &, const SourceInfo &, Environment &);
~RasterTileData();
void parse() override;
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index d9236b19b3..ec3a1b8e00 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -1,12 +1,14 @@
#include <mbgl/map/source.hpp>
#include <mbgl/map/map.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/raster.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/texture_pool.hpp>
-#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
@@ -32,7 +34,7 @@ Source::Source(SourceInfo& info_)
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
-void Source::load(Map& map, FileSource& fileSource) {
+void Source::load(Map &map, Environment &env) {
if (info.url.empty()) {
loaded = true;
return;
@@ -41,7 +43,7 @@ void Source::load(Map& map, FileSource& fileSource) {
util::ptr<Source> source = shared_from_this();
const std::string url = util::mapbox::normalizeSourceURL(info.url, map.getAccessToken());
- fileSource.request({ Resource::Kind::JSON, url }, **map.loop, [source, &map](const Response &res) {
+ env.request({ Resource::Kind::JSON, url }, [source, &map](const Response &res) {
if (res.status != Response::Successful) {
Log::Warning(Event::General, "Failed to load source TileJSON: %s", res.message.c_str());
return;
@@ -153,13 +155,11 @@ TileData::State Source::hasTile(const Tile::ID& id) {
return TileData::State::invalid;
}
-TileData::State Source::addTile(Map& map, uv::worker& worker,
- util::ptr<Style> style,
- GlyphAtlas& glyphAtlas, GlyphStore& glyphStore,
- SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite,
- FileSource& fileSource, uv_loop_t &loop, TexturePool& texturePool,
- const Tile::ID& id,
- std::function<void ()> callback) {
+TileData::State Source::addTile(Map &map, Environment &env, uv::worker &worker,
+ util::ptr<Style> style, GlyphAtlas &glyphAtlas,
+ GlyphStore &glyphStore, SpriteAtlas &spriteAtlas,
+ util::ptr<Sprite> sprite, TexturePool &texturePool,
+ const Tile::ID &id, std::function<void()> callback) {
const TileData::State state = hasTile(id);
if (state != TileData::State::invalid) {
@@ -190,14 +190,14 @@ TileData::State Source::addTile(Map& map, uv::worker& worker,
new_tile.data = std::make_shared<VectorTileData>(normalized_id, map.getMaxZoom(), style,
glyphAtlas, glyphStore,
spriteAtlas, sprite,
- info, fileSource);
+ info, env);
} else if (info.type == SourceType::Raster) {
- new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info, fileSource);
+ new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info, env);
} else {
throw std::runtime_error("source type not implemented");
}
- new_tile.data->request(worker, loop, map.getState().getPixelRatio(), callback);
+ new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
tile_data.emplace(new_tile.data->id, new_tile.data);
}
@@ -283,12 +283,16 @@ bool Source::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::
return false;
}
-void Source::update(Map& map, uv::worker& worker,
+void Source::update(Map &map,
+ Environment &env,
+ uv::worker &worker,
util::ptr<Style> style,
- GlyphAtlas& glyphAtlas, GlyphStore& glyphStore,
- SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite,
- TexturePool& texturePool, FileSource& fileSource, uv_loop_t& loop,
- std::function<void ()> callback) {
+ GlyphAtlas &glyphAtlas,
+ GlyphStore &glyphStore,
+ SpriteAtlas &spriteAtlas,
+ util::ptr<Sprite> sprite,
+ TexturePool &texturePool,
+ std::function<void()> callback) {
if (!loaded || map.getTime() <= updated)
return;
@@ -308,11 +312,8 @@ void Source::update(Map& map, uv::worker& worker,
// Add existing child/parent tiles if the actual tile is not yet loaded
for (const Tile::ID& id : required) {
- const TileData::State state = addTile(map, worker, style,
- glyphAtlas, glyphStore,
- spriteAtlas, sprite,
- fileSource, loop, texturePool,
- id, callback);
+ const TileData::State state = addTile(map, env, worker, style, glyphAtlas, glyphStore,
+ spriteAtlas, sprite, texturePool, id, callback);
if (state != TileData::State::parsed) {
// The tile we require is not yet loaded. Try to find a parent or
diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp
index 2c0e444225..061908b89d 100644
--- a/src/mbgl/map/source.hpp
+++ b/src/mbgl/map/source.hpp
@@ -18,11 +18,11 @@
namespace mbgl {
class Map;
+class Environment;
class GlyphAtlas;
class GlyphStore;
class SpriteAtlas;
class Sprite;
-class FileSource;
class TexturePool;
class Style;
class Painter;
@@ -34,13 +34,9 @@ class Source : public std::enable_shared_from_this<Source>, private util::noncop
public:
Source(SourceInfo&);
- void load(Map&, FileSource&);
- void update(Map&, uv::worker&,
- util::ptr<Style>,
- GlyphAtlas&, GlyphStore&,
- SpriteAtlas&, util::ptr<Sprite>,
- TexturePool&, FileSource&, uv_loop_t& loop,
- std::function<void ()> callback);
+ void load(Map &, Environment &);
+ void update(Map &, Environment &, uv::worker &, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
+ SpriteAtlas &, util::ptr<Sprite>, TexturePool &, std::function<void()> callback);
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void drawClippingMasks(Painter &painter);
@@ -59,13 +55,9 @@ private:
int32_t coveringZoomLevel(const TransformState&) const;
std::forward_list<Tile::ID> coveringTiles(const TransformState&) const;
- TileData::State addTile(Map&, uv::worker&,
- util::ptr<Style>,
- GlyphAtlas&, GlyphStore&,
- SpriteAtlas&, util::ptr<Sprite>,
- FileSource&, uv_loop_t &, TexturePool&,
- const Tile::ID&,
- std::function<void ()> callback);
+ TileData::State addTile(Map &, Environment &, uv::worker &, util::ptr<Style>, GlyphAtlas &,
+ GlyphStore &, SpriteAtlas &, util::ptr<Sprite>, TexturePool &,
+ const Tile::ID &, std::function<void()> callback);
TileData::State hasTile(const Tile::ID& id);
diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp
index 9543fe083a..4be92ff73f 100644
--- a/src/mbgl/map/sprite.cpp
+++ b/src/mbgl/map/sprite.cpp
@@ -5,7 +5,9 @@
#include <string>
#include <mbgl/platform/platform.hpp>
-#include <mbgl/storage/file_source.hpp>
+#include <mbgl/map/environment.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/std.hpp>
@@ -22,9 +24,9 @@ SpritePosition::SpritePosition(uint16_t x_, uint16_t y_, uint16_t width_, uint16
sdf(sdf_) {
}
-util::ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, FileSource& fileSource) {
+util::ptr<Sprite> Sprite::Create(const std::string &base_url, float pixelRatio, Environment &env) {
util::ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
- sprite->load(fileSource);
+ sprite->load(env);
return sprite;
}
@@ -51,7 +53,7 @@ Sprite::operator bool() const {
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
-void Sprite::load(FileSource& fileSource) {
+void Sprite::load(Environment &env) {
if (!valid) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
@@ -63,29 +65,25 @@ void Sprite::load(FileSource& fileSource) {
util::ptr<Sprite> sprite = shared_from_this();
- fileSource.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) {
+ env.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) {
if (res.status == Response::Successful) {
sprite->body = res.data;
sprite->parseJSON();
sprite->complete();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str());
- if (!sprite->future.valid()) {
- sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
- }
+ sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
}
});
- fileSource.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) {
+ env.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) {
if (res.status == Response::Successful) {
sprite->image = res.data;
sprite->parseImage();
sprite->complete();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str());
- if (!sprite->future.valid()) {
- sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
- }
+ sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
}
});
}
diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp
index d4b54ba1b5..cb0c274dee 100644
--- a/src/mbgl/map/sprite.hpp
+++ b/src/mbgl/map/sprite.hpp
@@ -14,7 +14,7 @@
namespace mbgl {
-class FileSource;
+class Environment;
class SpritePosition {
public:
@@ -34,11 +34,12 @@ public:
class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable {
private:
struct Key {};
- void load(FileSource& fileSource);
+ void load(Environment &env);
public:
Sprite(const Key &, const std::string& base_url, float pixelRatio);
- static util::ptr<Sprite> Create(const std::string& base_url, float pixelRatio, FileSource& fileSource);
+ static util::ptr<Sprite>
+ Create(const std::string &base_url, float pixelRatio, Environment &env);
const SpritePosition &getSpritePosition(const std::string& name) const;
diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp
index 9d48041239..53d36dfe8b 100644
--- a/src/mbgl/map/tile_data.cpp
+++ b/src/mbgl/map/tile_data.cpp
@@ -1,5 +1,6 @@
#include <mbgl/map/tile_data.hpp>
#include <mbgl/map/map.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/style/style_source.hpp>
#include <mbgl/util/token.hpp>
@@ -10,12 +11,12 @@
using namespace mbgl;
-TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& fileSource_)
+TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, Environment& env_)
: id(id_),
name(id),
state(State::initial),
source(source_),
- fileSource(fileSource_),
+ env(env_),
debugBucket(debugFontBuffer) {
// Initialize tile debug coordinates
debugFontBuffer.addText(name.c_str(), 50, 200, 5);
@@ -23,7 +24,7 @@ TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& f
TileData::~TileData() {
if (req) {
- fileSource.cancel(req);
+ env.cancelRequest(req);
}
}
@@ -31,8 +32,7 @@ const std::string TileData::toString() const {
return std::string { "[tile " } + name + "]";
}
-void TileData::request(uv::worker &worker, uv_loop_t &loop,
- float pixelRatio, std::function<void()> callback) {
+void TileData::request(uv::worker &worker, float pixelRatio, std::function<void()> callback) {
if (source.tiles.empty())
return;
@@ -53,9 +53,8 @@ void TileData::request(uv::worker &worker, uv_loop_t &loop,
state = State::loading;
- // Note: Somehow this feels slower than the change to request_http()
std::weak_ptr<TileData> weak_tile = shared_from_this();
- req = fileSource.request({ Resource::Kind::Tile, url }, &loop, [weak_tile, url, callback, &worker](const Response &res) {
+ req = env.request({ Resource::Kind::Tile, url }, [weak_tile, url, callback, &worker](const Response &res) {
util::ptr<TileData> tile = weak_tile.lock();
if (!tile || tile->state == State::obsolete) {
// noop. Tile is obsolete and we're now just waiting for the refcount
@@ -84,7 +83,7 @@ void TileData::cancel() {
state = State::obsolete;
}
if (req) {
- fileSource.cancel(req);
+ env.cancelRequest(req);
req = nullptr;
}
}
diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp
index fcd741a1f8..66c0fbba2e 100644
--- a/src/mbgl/map/tile_data.hpp
+++ b/src/mbgl/map/tile_data.hpp
@@ -23,7 +23,7 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Map;
-class FileSource;
+class Environment;
class Painter;
class SourceInfo;
class StyleLayer;
@@ -48,10 +48,10 @@ public:
};
public:
- TileData(Tile::ID const& id, const SourceInfo&, FileSource&);
+ TileData(Tile::ID const &id, const SourceInfo &, Environment &);
~TileData();
- void request(uv::worker&, uv_loop_t&, float pixelRatio, std::function<void ()> callback);
+ void request(uv::worker&, float pixelRatio, std::function<void ()> callback);
void reparse(uv::worker&, std::function<void ()> callback);
void cancel();
const std::string toString() const;
@@ -72,7 +72,7 @@ public:
public:
const SourceInfo& source;
- FileSource& fileSource;
+ Environment &env;
protected:
Request *req = nullptr;
diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp
index 68974aadf4..66fed0706b 100644
--- a/src/mbgl/map/vector_tile_data.cpp
+++ b/src/mbgl/map/vector_tile_data.cpp
@@ -14,8 +14,8 @@ VectorTileData::VectorTileData(Tile::ID const& id_,
float mapMaxZoom, util::ptr<Style> style_,
GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_,
SpriteAtlas& spriteAtlas_, util::ptr<Sprite> sprite_,
- const SourceInfo& source_, FileSource &fileSource_)
- : TileData(id_, source_, fileSource_),
+ const SourceInfo& source_, Environment &env_)
+ : TileData(id_, source_, env_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
spriteAtlas(spriteAtlas_),
diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp
index feb3c6238b..1c48fc5f23 100644
--- a/src/mbgl/map/vector_tile_data.hpp
+++ b/src/mbgl/map/vector_tile_data.hpp
@@ -30,11 +30,8 @@ class VectorTileData : public TileData {
friend class TileParser;
public:
- VectorTileData(Tile::ID const&,
- float mapMaxZoom, util::ptr<Style>,
- GlyphAtlas&, GlyphStore&,
- SpriteAtlas&, util::ptr<Sprite>,
- const SourceInfo&, FileSource &);
+ VectorTileData(Tile::ID const &, float mapMaxZoom, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
+ SpriteAtlas &, util::ptr<Sprite>, const SourceInfo &, Environment &);
~VectorTileData();
void parse() override;
diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp
index c6b201b559..9f70ac9943 100644
--- a/src/mbgl/storage/default_file_source.cpp
+++ b/src/mbgl/storage/default_file_source.cpp
@@ -50,6 +50,10 @@ struct DefaultFileSource::ResultAction {
struct DefaultFileSource::StopAction {
};
+struct DefaultFileSource::AbortAction {
+ const Environment &env;
+};
+
DefaultFileSource::DefaultFileSource(FileCache *cache_, const std::string &root)
: assetRoot(root.empty() ? platform::assetRoot() : root),
@@ -105,8 +109,9 @@ SharedRequestBase *DefaultFileSource::find(const Resource &resource) {
return nullptr;
}
-Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Callback callback) {
- auto req = new Request(resource, l, std::move(callback));
+Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, const Environment &env,
+ Callback callback) {
+ auto req = new Request(resource, l, env, std::move(callback));
// This function can be called from any thread. Make sure we're executing the actual call in the
// file source loop by sending it over the queue. It will be processed in processAction().
@@ -114,8 +119,9 @@ Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Call
return req;
}
-void DefaultFileSource::request(const Resource &resource, Callback callback) {
- auto req = new Request(resource, nullptr, std::move(callback));
+void DefaultFileSource::request(const Resource &resource, const Environment &env,
+ Callback callback) {
+ auto req = new Request(resource, nullptr, env, std::move(callback));
// This function can be called from any thread. Make sure we're executing the actual call in the
// file source loop by sending it over the queue. It will be processed in processAction().
@@ -130,6 +136,11 @@ void DefaultFileSource::cancel(Request *req) {
queue->send(RemoveRequestAction{ req });
}
+void DefaultFileSource::abort(const Environment &env) {
+ queue->send(AbortAction{ env });
+}
+
+
void DefaultFileSource::process(AddRequestAction &action) {
const Resource &resource = action.request->resource;
@@ -209,18 +220,45 @@ void DefaultFileSource::process(ResultAction &action) {
}
}
+// A stop action means the file source is about to be destructed. We need to cancel all requests
+// for all environments.
void DefaultFileSource::process(StopAction &) {
- // Cancel all remaining requests.
- for (auto it : pending) {
- it.second->unsubscribeAll();
- }
- pending.clear();
-
+ // There may not be any pending requests in this file source anymore. You must terminate all
+ // Map objects before deleting the FileSource.
+ assert(pending.empty());
assert(queue);
queue->stop();
queue = nullptr;
}
+// Aborts all requests that are part of the current environment.
+void DefaultFileSource::process(AbortAction &action) {
+ // Construct a cancellation response.
+ auto res = util::make_unique<Response>();
+ res->status = Response::Error;
+ res->message = "Environment is terminating";
+ std::shared_ptr<const Response> response = std::move(res);
+
+ // Iterate through all pending requests and remove them in case they're abandoned.
+ util::erase_if(pending, [&](const std::pair<Resource, SharedRequestBase *> &it) -> bool {
+ // Obtain all pending requests that are in the current environment.
+ const auto aborted = it.second->removeAllInEnvironment(action.env);
+
+ // Notify all observers.
+ for (auto req : aborted) {
+ req->notify(response);
+ }
+
+ // Finally, remove all requests that are now abandoned.
+ if (it.second->abandoned()) {
+ it.second->cancel();
+ return true;
+ } else {
+ return false;
+ }
+ });
+}
+
void DefaultFileSource::notify(SharedRequestBase *sharedRequest,
const std::set<Request *> &observers,
std::shared_ptr<const Response> response, FileCache::Hint hint) {
@@ -235,8 +273,8 @@ void DefaultFileSource::notify(SharedRequestBase *sharedRequest,
}
// Notify all observers.
- for (auto it : observers) {
- it->notify(response);
+ for (auto req : observers) {
+ req->notify(response);
}
}
diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp
index de18138ec2..48b5b774d2 100644
--- a/src/mbgl/storage/request.cpp
+++ b/src/mbgl/storage/request.cpp
@@ -13,8 +13,8 @@
namespace mbgl {
// Note: This requires that loop is running in the current thread (or not yet running).
-Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_)
- : callback(callback_), resource(resource_) {
+Request::Request(const Resource &resource_, uv_loop_t *loop, const Environment &env_, Callback callback_)
+ : callback(callback_), resource(resource_), env(env_) {
// When there is no loop supplied (== nullptr), the callback will be fired in an arbitrary
// thread (the thread notify() is called from) rather than kicking back to the calling thread.
if (loop) {
diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp
index f89f42e909..ab1776c04b 100644
--- a/src/mbgl/text/glyph_store.cpp
+++ b/src/mbgl/text/glyph_store.cpp
@@ -1,5 +1,6 @@
#include <mbgl/text/glyph_store.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/utf.hpp>
@@ -137,9 +138,11 @@ void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float m
align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
}
-GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, FileSource& fileSource)
- : future(promise.get_future().share())
-{
+GlyphPBF::GlyphPBF(const std::string &glyphURL,
+ const std::string &fontStack,
+ GlyphRange glyphRange,
+ Environment &env)
+ : future(promise.get_future().share()) {
// Load the glyph set URL
std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string {
if (name == "fontstack") return util::percentEncode(fontStack);
@@ -148,7 +151,7 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl
});
// The prepare call jumps back to the main thread.
- fileSource.request({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) {
+ env.requestAsync({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) {
if (res.status != Response::Successful) {
// Something went wrong with loading the glyph pbf. Pass on the error to the future listeners.
const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + res.message;
@@ -223,7 +226,7 @@ void GlyphPBF::parse(FontStack &stack) {
data.clear();
}
-GlyphStore::GlyphStore(FileSource& fileSource_) : fileSource(fileSource_), mtx(util::make_unique<uv::mutex>()) {}
+GlyphStore::GlyphStore(Environment& env_) : env(env_), mtx(util::make_unique<uv::mutex>()) {}
void GlyphStore::setURL(const std::string &url) {
glyphURL = url;
@@ -265,7 +268,7 @@ std::shared_future<GlyphPBF &> GlyphStore::loadGlyphRange(const std::string &fon
auto range_it = rangeSets.find(range);
if (range_it == rangeSets.end()) {
// We don't have this glyph set yet for this font stack.
- range_it = rangeSets.emplace(range, util::make_unique<GlyphPBF>(glyphURL, fontStack, range, fileSource)).first;
+ range_it = rangeSets.emplace(range, util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env)).first;
}
return range_it->second->getFuture();
diff --git a/src/mbgl/text/glyph_store.hpp b/src/mbgl/text/glyph_store.hpp
index 6839045d61..406234241d 100644
--- a/src/mbgl/text/glyph_store.hpp
+++ b/src/mbgl/text/glyph_store.hpp
@@ -17,6 +17,7 @@
namespace mbgl {
class FileSource;
+class Environment;
class SDFGlyph {
public:
@@ -49,7 +50,10 @@ private:
class GlyphPBF {
public:
- GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, FileSource& fileSource);
+ GlyphPBF(const std::string &glyphURL,
+ const std::string &fontStack,
+ GlyphRange glyphRange,
+ Environment &env);
private:
GlyphPBF(const GlyphPBF &) = delete;
@@ -72,7 +76,7 @@ private:
// Manages Glyphrange PBF loading.
class GlyphStore {
public:
- GlyphStore(FileSource& fileSource);
+ GlyphStore(Environment &);
// Block until all specified GlyphRanges of the specified font stack are loaded.
void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
@@ -88,7 +92,7 @@ private:
FontStack &createFontStack(const std::string &fontStack);
std::string glyphURL;
- FileSource& fileSource;
+ Environment &env;
std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges;
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;
std::unique_ptr<uv::mutex> mtx;
diff --git a/test/storage/cache_response.cpp b/test/storage/cache_response.cpp
index a0b5ba31c1..ac0dc4c565 100644
--- a/test/storage/cache_response.cpp
+++ b/test/storage/cache_response.cpp
@@ -14,8 +14,9 @@ TEST_F(Storage, CacheResponse) {
DefaultFileSource fs(&cache, uv_default_loop());
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/cache" };
+ auto &env = *static_cast<const Environment *>(nullptr);
- fs.request(resource, uv_default_loop(), [&](const Response &res) {
+ fs.request(resource, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response 1", res.data);
EXPECT_LT(0, res.expires);
@@ -23,7 +24,7 @@ TEST_F(Storage, CacheResponse) {
EXPECT_EQ("", res.etag);
EXPECT_EQ("", res.message);
- fs.request(resource, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(resource, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(res.status, res2.status);
EXPECT_EQ(res.data, res2.data);
EXPECT_EQ(res.expires, res2.expires);
diff --git a/test/storage/cache_revalidate.cpp b/test/storage/cache_revalidate.cpp
index 530b7325b5..bd32042b94 100644
--- a/test/storage/cache_revalidate.cpp
+++ b/test/storage/cache_revalidate.cpp
@@ -15,8 +15,10 @@ TEST_F(Storage, CacheRevalidate) {
SQLiteCache cache(":memory:");
DefaultFileSource fs(&cache);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const Resource revalidateSame { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
- fs.request(revalidateSame, uv_default_loop(), [&](const Response &res) {
+ fs.request(revalidateSame, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response", res.data);
EXPECT_EQ(0, res.expires);
@@ -24,7 +26,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("snowfall", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateSame, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(revalidateSame, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(Response::Successful, res2.status);
EXPECT_EQ("Response", res2.data);
// We use this to indicate that a 304 reply came back.
@@ -38,8 +40,9 @@ TEST_F(Storage, CacheRevalidate) {
});
});
- const Resource revalidateModified { Resource::Unknown, "http://127.0.0.1:3000/revalidate-modified" };
- fs.request(revalidateModified, uv_default_loop(), [&](const Response &res) {
+ const Resource revalidateModified{ Resource::Unknown,
+ "http://127.0.0.1:3000/revalidate-modified" };
+ fs.request(revalidateModified, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response", res.data);
EXPECT_EQ(0, res.expires);
@@ -47,7 +50,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateModified, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(revalidateModified, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(Response::Successful, res2.status);
EXPECT_EQ("Response", res2.data);
// We use this to indicate that a 304 reply came back.
@@ -61,7 +64,7 @@ TEST_F(Storage, CacheRevalidate) {
});
const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" };
- fs.request(revalidateEtag, uv_default_loop(), [&](const Response &res) {
+ fs.request(revalidateEtag, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response 1", res.data);
EXPECT_EQ(0, res.expires);
@@ -69,7 +72,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("response-1", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateEtag, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(revalidateEtag, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(Response::Successful, res2.status);
EXPECT_EQ("Response 2", res2.data);
EXPECT_EQ(0, res2.expires);
diff --git a/test/storage/directory_reading.cpp b/test/storage/directory_reading.cpp
index 3ee4dd1721..a955648462 100644
--- a/test/storage/directory_reading.cpp
+++ b/test/storage/directory_reading.cpp
@@ -15,7 +15,10 @@ TEST_F(Storage, AssetReadDirectory) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
- fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(), [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(),
+ env, [&](const Response &res) {
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -24,7 +27,7 @@ TEST_F(Storage, AssetReadDirectory) {
#ifdef MBGL_ASSET_ZIP
EXPECT_EQ("No such file", res.message);
#elif MBGL_ASSET_FS
- EXPECT_EQ("illegal operation on a directory", res.message);
+ EXPECT_EQ("illegal operation on a directory", res.message);
#endif
ReadDirectory.finish();
});
diff --git a/test/storage/file_reading.cpp b/test/storage/file_reading.cpp
index 7e14fdcb15..cca072b27a 100644
--- a/test/storage/file_reading.cpp
+++ b/test/storage/file_reading.cpp
@@ -16,9 +16,10 @@ TEST_F(Storage, AssetEmptyFile) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
- fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" },
- uv_default_loop(),
- [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" }, uv_default_loop(),
+ env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -42,9 +43,10 @@ TEST_F(Storage, AssetNonEmptyFile) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
+ auto &env = *static_cast<const Environment *>(nullptr);
+
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/nonempty" },
- uv_default_loop(),
- [&](const Response &res) {
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(16ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -69,9 +71,10 @@ TEST_F(Storage, AssetNonExistentFile) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
+ auto &env = *static_cast<const Environment *>(nullptr);
+
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/does_not_exist" },
- uv_default_loop(),
- [&](const Response &res) {
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_cancel.cpp b/test/storage/http_cancel.cpp
index 7e485ec42d..d0dac8ccdb 100644
--- a/test/storage/http_cancel.cpp
+++ b/test/storage/http_cancel.cpp
@@ -14,9 +14,11 @@ TEST_F(Storage, HTTPCancel) {
DefaultFileSource fs(nullptr, uv_default_loop());
- auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &) {
- ADD_FAILURE() << "Callback should not be called";
- });
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ auto req =
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ [&](const Response &) { ADD_FAILURE() << "Callback should not be called"; });
fs.cancel(req);
HTTPCancel.finish();
@@ -31,11 +33,13 @@ TEST_F(Storage, HTTPCancelMultiple) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
- auto req2 = fs.request(resource, uv_default_loop(), [&](const Response &) {
+
+ auto req2 = fs.request(resource, uv_default_loop(), env, [&](const Response &) {
ADD_FAILURE() << "Callback should not be called";
});
- fs.request(resource, uv_default_loop(), [&](const Response &res) {
+ fs.request(resource, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_coalescing.cpp b/test/storage/http_coalescing.cpp
index 592c65f8d6..9eaea70e11 100644
--- a/test/storage/http_coalescing.cpp
+++ b/test/storage/http_coalescing.cpp
@@ -14,6 +14,8 @@ TEST_F(Storage, HTTPCoalescing) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
static const Response *reference = nullptr;
const auto complete = [&](const Response &res) {
@@ -41,7 +43,7 @@ TEST_F(Storage, HTTPCoalescing) {
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
for (int i = 0; i < total; i++) {
- fs.request(resource, uv_default_loop(), complete);
+ fs.request(resource, uv_default_loop(), env, complete);
}
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
diff --git a/test/storage/http_environment.cpp b/test/storage/http_environment.cpp
new file mode 100644
index 0000000000..efd7b78ad4
--- /dev/null
+++ b/test/storage/http_environment.cpp
@@ -0,0 +1,54 @@
+#include "storage.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/network_status.hpp>
+
+#include <cmath>
+
+TEST_F(Storage, HTTPCancelEnvironment) {
+ SCOPED_TEST(HTTPRetainedEnvironment)
+ SCOPED_TEST(HTTPCanceledEnvironment)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ // Create two fake environment pointers. The FileSource implementation treats these as opaque
+ // pointers and doesn't reach into them.
+ auto &env1 = *reinterpret_cast<const Environment *>(1);
+ auto &env2 = *reinterpret_cast<const Environment *>(2);
+
+ const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/delayed" };
+
+ // Environment 1
+ fs.request(resource, uv_default_loop(), env1, [&](const Response &res) {
+ // This environment gets aborted below. This means the request is marked as failing and
+ // will return an error here.
+ EXPECT_EQ(Response::Error, res.status);
+ EXPECT_EQ("", res.data);
+ EXPECT_EQ(0, res.expires);
+ EXPECT_EQ(0, res.modified);
+ EXPECT_EQ("", res.etag);
+ EXPECT_EQ("Environment is terminating", res.message);
+ HTTPCanceledEnvironment.finish();
+ });
+
+ // Environment 2
+ fs.request(resource, uv_default_loop(), env2, [&](const Response &res) {
+ // The same request as above, but in a different environment which doesn't get aborted. This
+ // means the request should succeed.
+ EXPECT_EQ(Response::Successful, res.status);
+ EXPECT_EQ("Response", res.data);
+ EXPECT_EQ(0, res.expires);
+ EXPECT_EQ(0, res.modified);
+ EXPECT_EQ("", res.etag);
+ EXPECT_EQ("", res.message);
+ HTTPRetainedEnvironment.finish();
+ });
+
+ fs.abort(env1);
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_error.cpp b/test/storage/http_error.cpp
index 498f1eae6b..abaeff5396 100644
--- a/test/storage/http_error.cpp
+++ b/test/storage/http_error.cpp
@@ -22,9 +22,12 @@ TEST_F(Storage, HTTPError) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
auto start = uv_hrtime();
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, uv_default_loop(),
+ env, [&](const Response &res) {
const auto duration = double(uv_hrtime() - start) / 1e9;
EXPECT_LT(1, duration) << "Backoff timer didn't wait 1 second";
EXPECT_GT(1.2, duration) << "Backoff timer fired too late";
@@ -38,13 +41,21 @@ TEST_F(Storage, HTTPError) {
HTTPTemporaryError.finish();
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), env,
+ [&](const Response &res) {
const auto duration = double(uv_hrtime() - start) / 1e9;
// 1.5 seconds == 4 retries, with a 500ms timeout (see above).
EXPECT_LT(1.5, duration) << "Resource wasn't retried the correct number of times";
EXPECT_GT(1.7, duration) << "Resource wasn't retried the correct number of times";
EXPECT_EQ(Response::Error, res.status);
- EXPECT_TRUE(res.message == "Couldn't connect to server: couldn't connect to host" || res.message == "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)") << res.message;
+#ifdef MBGL_HTTP_NSURL
+ EXPECT_STREQ(res.message.c_str(), "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)");
+#elif MBGL_HTTP_CURL
+ const std::string prefix { "Couldn't connect to server: " };
+ EXPECT_STREQ(prefix.c_str(), res.message.substr(0, prefix.size()).c_str()) << "Full message is: \"" << res.message << "\"";
+#else
+ FAIL();
+#endif
EXPECT_EQ("", res.data);
EXPECT_EQ(0, res.expires);
EXPECT_EQ(0, res.modified);
@@ -52,7 +63,6 @@ TEST_F(Storage, HTTPError) {
HTTPConnectionError.finish();
});
-
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
uv_timer_stop(&statusChange);
diff --git a/test/storage/http_header_parsing.cpp b/test/storage/http_header_parsing.cpp
index 655348c877..271460387c 100644
--- a/test/storage/http_header_parsing.cpp
+++ b/test/storage/http_header_parsing.cpp
@@ -14,7 +14,11 @@ TEST_F(Storage, HTTPHeaderParsing) {
DefaultFileSource fs(nullptr, uv_default_loop());
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" }, uv_default_loop(), [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown,
+ "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" },
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(1420797926, res.expires);
@@ -24,11 +28,11 @@ TEST_F(Storage, HTTPHeaderParsing) {
HTTPExpiresTest.finish();
});
-
int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" },
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
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";
diff --git a/test/storage/http_load.cpp b/test/storage/http_load.cpp
index fff662da29..2680daf93b 100644
--- a/test/storage/http_load.cpp
+++ b/test/storage/http_load.cpp
@@ -11,13 +11,17 @@ TEST_F(Storage, HTTPLoad) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const int concurrency = 50;
const int max = 10000;
int number = 1;
std::function<void()> req = [&]() {
const auto current = number++;
- fs.request({ Resource::Unknown, std::string("http://127.0.0.1:3000/load/") + std::to_string(current) }, uv_default_loop(), [&, current](const Response &res) {
+ fs.request({ Resource::Unknown,
+ std::string("http://127.0.0.1:3000/load/") + std::to_string(current) },
+ uv_default_loop(), env, [&, current](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(std::string("Request ") + std::to_string(current), res.data);
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_noloop.cpp b/test/storage/http_noloop.cpp
index c71fd0bc26..cd4ebeb2c4 100644
--- a/test/storage/http_noloop.cpp
+++ b/test/storage/http_noloop.cpp
@@ -12,6 +12,8 @@ TEST_F(Storage, HTTPNoLoop) {
DefaultFileSource fs(nullptr);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const auto mainThread = uv_thread_self();
// Async handle that keeps the main loop alive until the thread finished
@@ -20,7 +22,8 @@ TEST_F(Storage, HTTPNoLoop) {
uv::close(as);
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, nullptr, env,
+ [&](const Response &res) {
EXPECT_NE(uv_thread_self(), mainThread) << "Response was called in the same thread";
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
diff --git a/test/storage/http_other_loop.cpp b/test/storage/http_other_loop.cpp
index 06cc6f7476..9ad673cf79 100644
--- a/test/storage/http_other_loop.cpp
+++ b/test/storage/http_other_loop.cpp
@@ -12,7 +12,10 @@ TEST_F(Storage, HTTPOtherLoop) {
// This file source launches a separate thread to do the processing.
DefaultFileSource fs(nullptr);
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_reading.cpp b/test/storage/http_reading.cpp
index ad87db15ff..a6a5775825 100644
--- a/test/storage/http_reading.cpp
+++ b/test/storage/http_reading.cpp
@@ -12,9 +12,12 @@ TEST_F(Storage, HTTPReading) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const auto mainThread = uv_thread_self();
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ [&](const Response &res) {
EXPECT_EQ(uv_thread_self(), mainThread);
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
@@ -25,7 +28,8 @@ TEST_F(Storage, HTTPReading) {
HTTPTest.finish();
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(),
+ env, [&](const Response &res) {
EXPECT_EQ(uv_thread_self(), mainThread);
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ("HTTP status code 404", res.message);
@@ -45,7 +49,10 @@ TEST_F(Storage, HTTPNoCallback) {
DefaultFileSource fs(nullptr, uv_default_loop());
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), nullptr);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ nullptr);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
@@ -59,7 +66,9 @@ TEST_F(Storage, HTTPNoCallbackNoLoop) {
DefaultFileSource fs(nullptr, uv_default_loop());
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, nullptr);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, env, nullptr);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
diff --git a/test/storage/server.js b/test/storage/server.js
index 33d5a82985..1ee5363196 100755
--- a/test/storage/server.js
+++ b/test/storage/server.js
@@ -85,6 +85,13 @@ app.get('/temporary-error', function(req, res) {
temporaryErrorCounter++;
});
+app.get('/delayed', function(req, res) {
+ setTimeout(function() {
+ res.status(200).send('Response');
+ }, 200);
+});
+
+
app.get('/load/:number(\\d+)', function(req, res) {
res.send('Request ' + req.params.number);
});
diff --git a/test/test.gyp b/test/test.gyp
index 1d80a4f830..7759caced8 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -56,6 +56,7 @@
'storage/file_reading.cpp',
'storage/http_cancel.cpp',
'storage/http_coalescing.cpp',
+ 'storage/http_environment.cpp',
'storage/http_error.cpp',
'storage/http_header_parsing.cpp',
'storage/http_load.cpp',