summaryrefslogtreecommitdiff
path: root/src/storage/http_request.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/storage/http_request.cpp')
-rw-r--r--src/storage/http_request.cpp112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/storage/http_request.cpp b/src/storage/http_request.cpp
new file mode 100644
index 0000000000..56eef9268c
--- /dev/null
+++ b/src/storage/http_request.cpp
@@ -0,0 +1,112 @@
+#include <mbgl/storage/http_request.hpp>
+#include <mbgl/storage/request.hpp>
+#include <mbgl/storage/sqlite_store.hpp>
+#include <mbgl/storage/http_request_baton.hpp>
+
+#include <uv.h>
+
+#include <cassert>
+#include <chrono>
+
+namespace mbgl {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+
+struct CacheRequestBaton {
+ HTTPRequest *request = nullptr;
+ std::string path;
+ uv_loop_t *loop;
+};
+
+HTTPRequest::HTTPRequest(ResourceType type_, const std::string &path, uv_loop_t *loop, std::shared_ptr<SQLiteStore> store_)
+ : BaseRequest(), thread_id(uv_thread_self()), store(store_), type(type_) {
+ cache_baton = new CacheRequestBaton;
+ cache_baton->request = this;
+ cache_baton->path = path;
+ cache_baton->loop = loop;
+ store->get(path, [](std::unique_ptr<Response> &&response, void *ptr) {
+ // Wrap in a unique_ptr, so it'll always get auto-destructed.
+ std::unique_ptr<CacheRequestBaton> baton((CacheRequestBaton *)ptr);
+ if (baton->request) {
+ assert(uv_thread_self() == baton->request->thread_id);
+ baton->request->loadedCacheEntry(std::move(response));
+ if (baton->request) {
+ // If we called notify(), the request object may already have ceased to exist.
+ baton->request->cache_baton = nullptr;
+ }
+ }
+ }, cache_baton);
+}
+
+void HTTPRequest::loadedCacheEntry(std::unique_ptr<Response> &&response) {
+ if (response) {
+ // This entry was stored in the cache. Now determine if we need to revalidate.
+ const int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch()).count();
+ if (response->expires > now) {
+ if (cache_baton->request) {
+ cache_baton->request->response = std::move(response);
+ cache_baton->request->notify();
+ // Note: after calling notify(), the request object may cease to exist.
+ }
+ // This HTTPRequest is completed.
+ return;
+ } else {
+ // TODO: notify with preliminary results.
+ }
+ }
+
+ // Either, the cache entry is expired, or there is no cached version. In both cases,
+ // we need to do a new request. The requesting code will issue a conditional request in case
+ // we already have a modified date on the response object.
+ http_baton = new HTTPRequestBaton;
+ http_baton->request = this;
+ http_baton->path = std::move(cache_baton->path);
+ http_baton->async = new uv_async_t;
+ http_baton->response = std::move(response);
+ http_baton->async->data = http_baton;
+ uv_async_init(cache_baton->loop, http_baton->async, [](uv_async_t *async) {
+ std::unique_ptr<HTTPRequestBaton> baton((HTTPRequestBaton *)async->data);
+ if (baton->request) {
+ assert(uv_thread_self() == baton->request->thread_id);
+ baton->request->response = std::move(baton->response);
+ if (baton->not_modified) {
+ baton->request->store->updateExpiration(baton->path, baton->request->response->expires);
+ } else {
+ baton->request->store->put(baton->path, baton->request->type, *baton->request->response);
+ }
+ baton->request->notify();
+ // Note: after calling notify(), the baton object may cease to exist.
+ }
+
+ uv_close((uv_handle_t *)async, [](uv_handle_t *handle) {
+ delete (uv_async_t *)handle;
+ });
+ });
+ http_baton->start();
+}
+
+HTTPRequest::~HTTPRequest() {
+ assert(uv_thread_self() == thread_id);
+
+ if (cache_baton) {
+ // Make sure that this object doesn't accidentally get accessed when it is destructed before
+ // the callback returned. They are being run in the same thread, so just setting it to
+ // null is sufficient.
+ // Note: We don't manually delete the CacheRequestBaton since it'll be deleted by the
+ // callback.
+ cache_baton->request = nullptr;
+ }
+
+ if (http_baton) {
+ http_baton->request = nullptr;
+ http_baton->cancel();
+ }
+}
+
+#pragma clang diagnostic pop
+
+}