1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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
}
|