• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "cache_proxy.h"
17 
18 #include <atomic>
19 #include <condition_variable>
20 #include <mutex>
21 #include <thread>
22 
23 #include "base64_utils.h"
24 #include "constant.h"
25 #include "casche_constant.h"
26 #include "lru_cache_disk_handler.h"
27 #include "netstack_common_utils.h"
28 #include "netstack_log.h"
29 #include "request_context.h"
30 
31 #ifdef HTTP_CACHE_FILE_PATH_USE_BASE
32 static constexpr const char *CACHE_FILE = "/data/storage/el2/base/cache/cache.json";
33 #else
34 static constexpr const char *CACHE_FILE = "/data/storage/el2/database/cache.json";
35 #endif
36 static constexpr int32_t WRITE_INTERVAL = 60;
37 
38 namespace OHOS::NetStack::Http {
39 std::mutex g_diskCacheMutex;
40 std::mutex g_cacheNeedRunMutex;
41 std::atomic_bool g_cacheNeedRun(false);
42 std::atomic_bool g_cacheIsRunning(false);
43 std::condition_variable g_cacheThreadCondition;
44 std::condition_variable g_cacheNeedRunCondition;
45 static LRUCacheDiskHandler DISK_LRU_CACHE(CACHE_FILE, 0); // NOLINT(cert-err58-cpp)
46 
CacheProxy(HttpRequestOptions & requestOptions)47 CacheProxy::CacheProxy(HttpRequestOptions &requestOptions) : strategy_(requestOptions)
48 {
49     std::string str = requestOptions.GetUrl() + HttpConstant::HTTP_LINE_SEPARATOR +
50                       CommonUtils::ToLower(requestOptions.GetMethod()) + HttpConstant::HTTP_LINE_SEPARATOR;
51     for (const auto &p : requestOptions.GetHeader()) {
52         if (p.first == IF_NONE_MATCH || p.first == IF_MODIFIED_SINCE) {
53             continue;
54         }
55         str += p.first + HttpConstant::HTTP_HEADER_SEPARATOR + p.second + HttpConstant::HTTP_LINE_SEPARATOR;
56     }
57     str += std::to_string(requestOptions.GetHttpVersion());
58     key_ = Base64::Encode(str);
59 }
60 
ReadResponseFromCache(RequestContext * context)61 bool CacheProxy::ReadResponseFromCache(RequestContext *context)
62 {
63     if (!g_cacheIsRunning.load()) {
64         return false;
65     }
66 
67     if (!strategy_.CouldUseCache()) {
68         NETSTACK_LOGI("only GET/HEAD method or header has [Range] can use cache");
69         return false;
70     }
71 
72     auto responseFromCache = DISK_LRU_CACHE.Get(key_);
73     if (responseFromCache.empty()) {
74         NETSTACK_LOGI("no cache with this request");
75         return false;
76     }
77     HttpResponse cachedResponse;
78     cachedResponse.SetRawHeader(Base64::Decode(responseFromCache[HttpConstant::RESPONSE_KEY_HEADER]));
79     cachedResponse.SetResult(Base64::Decode(responseFromCache[HttpConstant::RESPONSE_KEY_RESULT]));
80     cachedResponse.SetCookies(Base64::Decode(responseFromCache[HttpConstant::RESPONSE_KEY_COOKIES]));
81     cachedResponse.SetResponseTime(Base64::Decode(responseFromCache[HttpConstant::RESPONSE_TIME]));
82     cachedResponse.SetRequestTime(Base64::Decode(responseFromCache[HttpConstant::REQUEST_TIME]));
83     cachedResponse.SetResponseCode(static_cast<uint32_t>(ResponseCode::OK));
84     cachedResponse.ParseHeaders();
85 
86     CacheStatus status = strategy_.RunStrategy(cachedResponse);
87     if (status == CacheStatus::FRESH) {
88         context->response = cachedResponse;
89         NETSTACK_LOGI("cache is FRESH");
90         return true;
91     }
92     if (status == CacheStatus::STALE) {
93         NETSTACK_LOGI("cache is STATE, we try to talk to the server");
94         context->SetCacheResponse(cachedResponse);
95         return false;
96     }
97     NETSTACK_LOGD("cache should not be used");
98     return false;
99 }
100 
WriteResponseToCache(const HttpResponse & response)101 void CacheProxy::WriteResponseToCache(const HttpResponse &response)
102 {
103     if (!g_cacheIsRunning.load()) {
104         return;
105     }
106 
107     if (!strategy_.IsCacheable(response)) {
108         NETSTACK_LOGE("do not cache this response");
109         return;
110     }
111     std::unordered_map<std::string, std::string> cacheResponse;
112     cacheResponse[HttpConstant::RESPONSE_KEY_HEADER] = Base64::Encode(response.GetRawHeader());
113     cacheResponse[HttpConstant::RESPONSE_KEY_RESULT] = Base64::Encode(response.GetResult());
114     cacheResponse[HttpConstant::RESPONSE_KEY_COOKIES] = Base64::Encode(response.GetCookies());
115     cacheResponse[HttpConstant::RESPONSE_TIME] = Base64::Encode(response.GetResponseTime());
116     cacheResponse[HttpConstant::REQUEST_TIME] = Base64::Encode(response.GetRequestTime());
117 
118     DISK_LRU_CACHE.Put(key_, cacheResponse);
119 }
120 
RunCache()121 void CacheProxy::RunCache()
122 {
123     RunCacheWithSize(MAX_DISK_CACHE_SIZE);
124 }
125 
RunCacheWithSize(size_t capacity)126 void CacheProxy::RunCacheWithSize(size_t capacity)
127 {
128     if (g_cacheIsRunning.load()) {
129         return;
130     }
131     DISK_LRU_CACHE.SetCapacity(capacity);
132 
133     g_cacheNeedRun.store(true);
134 
135     DISK_LRU_CACHE.ReadCacheFromJsonFile();
136 
137     std::thread([]() {
138         g_cacheIsRunning.store(true);
139         while (g_cacheNeedRun.load()) {
140             std::unique_lock<std::mutex> lock(g_cacheNeedRunMutex);
141             g_cacheNeedRunCondition.wait_for(lock, std::chrono::seconds(WRITE_INTERVAL),
142                                              [] { return !g_cacheNeedRun.load(); });
143 
144             DISK_LRU_CACHE.WriteCacheToJsonFile();
145         }
146 
147         g_cacheIsRunning.store(false);
148         g_cacheThreadCondition.notify_all();
149     }).detach();
150 }
151 
FlushCache()152 void CacheProxy::FlushCache()
153 {
154     if (!g_cacheIsRunning.load()) {
155         return;
156     }
157     DISK_LRU_CACHE.WriteCacheToJsonFile();
158 }
159 
StopCacheAndDelete()160 void CacheProxy::StopCacheAndDelete()
161 {
162     if (!g_cacheIsRunning.load()) {
163         return;
164     }
165     g_cacheNeedRun.store(false);
166     g_cacheNeedRunCondition.notify_all();
167 
168     std::unique_lock<std::mutex> lock(g_diskCacheMutex);
169     g_cacheThreadCondition.wait(lock, [] { return !g_cacheIsRunning.load(); });
170     DISK_LRU_CACHE.Delete();
171 }
172 } // namespace OHOS::NetStack::Http
173