1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/appcache/appcache_cache_test_helper.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/threading/thread_task_runner_handle.h"
11 #include "content/browser/appcache/appcache_response_info.h"
12 #include "net/base/io_buffer.h"
13 #include "net/http/http_response_headers.h"
14
15 namespace content {
16
17 namespace {
18 const int kAppCacheFetchBufferSize = 32768;
19 } // namespace
20
CacheEntry(int types,std::string expect_if_modified_since,std::string expect_if_none_match,bool headers_allowed,std::unique_ptr<net::HttpResponseInfo> response_info,const std::string & body)21 AppCacheCacheTestHelper::CacheEntry::CacheEntry(
22 int types,
23 std::string expect_if_modified_since,
24 std::string expect_if_none_match,
25 bool headers_allowed,
26 std::unique_ptr<net::HttpResponseInfo> response_info,
27 const std::string& body)
28 : types(types),
29 expect_if_modified_since(expect_if_modified_since),
30 expect_if_none_match(expect_if_none_match),
31 headers_allowed(headers_allowed),
32 response_info(std::move(response_info)),
33 body(body) {}
34
35 AppCacheCacheTestHelper::CacheEntry::~CacheEntry() = default;
36
37 // static
AddCacheEntry(CacheEntries * cache_entries,const GURL & url,int types,std::string expect_if_modified_since,std::string expect_if_none_match,bool headers_allowed,std::unique_ptr<net::HttpResponseInfo> response_info,const std::string & body)38 void AppCacheCacheTestHelper::AddCacheEntry(
39 CacheEntries* cache_entries,
40 const GURL& url,
41 int types,
42 std::string expect_if_modified_since,
43 std::string expect_if_none_match,
44 bool headers_allowed,
45 std::unique_ptr<net::HttpResponseInfo> response_info,
46 const std::string& body) {
47 cache_entries->emplace(
48 url, std::make_unique<AppCacheCacheTestHelper::CacheEntry>(
49 types, expect_if_modified_since, expect_if_none_match,
50 headers_allowed, std::move(response_info), body));
51 }
52
AppCacheCacheTestHelper(const MockAppCacheService * service,const GURL & manifest_url,AppCache * const cache,CacheEntries cache_entries,base::OnceCallback<void (int)> post_write_callback)53 AppCacheCacheTestHelper::AppCacheCacheTestHelper(
54 const MockAppCacheService* service,
55 const GURL& manifest_url,
56 AppCache* const cache,
57 CacheEntries cache_entries,
58 base::OnceCallback<void(int)> post_write_callback)
59 : service_(service),
60 manifest_url_(manifest_url),
61 cache_(cache),
62 cache_entries_(std::move(cache_entries)),
63 state_(State::kIdle),
64 post_write_callback_(std::move(post_write_callback)) {}
65
66 AppCacheCacheTestHelper::~AppCacheCacheTestHelper() = default;
67
PrepareForRead(AppCache * read_cache,base::OnceClosure post_read_callback)68 void AppCacheCacheTestHelper::PrepareForRead(
69 AppCache* read_cache,
70 base::OnceClosure post_read_callback) {
71 read_cache_ = read_cache;
72 post_read_callback_ = std::move(post_read_callback);
73 }
74
Read()75 void AppCacheCacheTestHelper::Read() {
76 DCHECK_EQ(state_, State::kIdle);
77 state_ = State::kReadInfo;
78 read_it_ = read_cache_->entries().begin();
79 read_cache_entries_.clear();
80 AsyncRead(0);
81 }
82
Write()83 void AppCacheCacheTestHelper::Write() {
84 DCHECK_EQ(state_, State::kIdle);
85 state_ = State::kWriteInfo;
86 write_it_ = cache_entries_.begin();
87 AsyncWrite(0);
88 }
89
OnResponseInfoLoaded(AppCacheResponseInfo * response_info,int64_t response_id)90 void AppCacheCacheTestHelper::OnResponseInfoLoaded(
91 AppCacheResponseInfo* response_info,
92 int64_t response_id) {
93 DCHECK(response_info);
94 DCHECK_EQ(read_entry_response_id_, response_id);
95
96 read_info_response_info_.reset();
97 read_info_response_info_ = response_info;
98 AsyncRead(0);
99 }
100
AsyncRead(int result)101 void AppCacheCacheTestHelper::AsyncRead(int result) {
102 DCHECK(state_ == State::kReadInfo || state_ == State::kReadData);
103 DCHECK_GE(result, 0);
104 if (read_it_ == read_cache_->entries().end()) {
105 state_ = State::kIdle;
106 std::move(post_read_callback_).Run();
107 return;
108 }
109 switch (state_) {
110 case State::kReadInfo: {
111 if (!read_info_response_info_) {
112 AppCacheEntry* entry = read_cache_->GetEntry(read_it_->first);
113 DCHECK(entry);
114 read_entry_response_id_ = entry->response_id();
115 service_->storage()->LoadResponseInfo(manifest_url_,
116 read_entry_response_id_, this);
117 } else {
118 // Result is in |read_info_response_info_|. Keep it there for now.
119 // Will be passed along after data is read.
120
121 // Prepare for next state.
122 state_ = State::kReadData;
123
124 // Trigger data read for |read_entry_response_id_|.
125 read_data_response_reader_ = service_->storage()->CreateResponseReader(
126 read_it_->first, read_entry_response_id_);
127 read_data_buffer_ =
128 base::MakeRefCounted<net::IOBuffer>(kAppCacheFetchBufferSize);
129 read_data_response_reader_->ReadData(
130 read_data_buffer_.get(), kAppCacheFetchBufferSize,
131 base::BindOnce(&AppCacheCacheTestHelper::AsyncRead,
132 base::Unretained(this)));
133 }
134 } break;
135 case State::kReadData: {
136 if (result > 0) {
137 read_data_loaded_data_.append(read_data_buffer_->data(), result);
138 read_data_response_reader_->ReadData(
139 read_data_buffer_.get(), kAppCacheFetchBufferSize,
140 base::BindOnce(&AppCacheCacheTestHelper::AsyncRead,
141 base::Unretained(this)));
142 } else {
143 // Result is in |read_data_loaded_data_|.
144
145 std::unique_ptr<net::HttpResponseInfo> http_response_info =
146 std::make_unique<net::HttpResponseInfo>(
147 read_info_response_info_->http_response_info());
148 AppCacheCacheTestHelper::AddCacheEntry(
149 &read_cache_entries_, read_it_->first, AppCacheEntry::EXPLICIT,
150 /*expect_if_modified_since=*/std::string(),
151 /*expect_if_none_match=*/std::string(), /*headers_allowed=*/false,
152 std::move(http_response_info), read_data_loaded_data_);
153
154 // Reset after read.
155 read_info_response_info_.reset();
156 read_entry_response_id_ = 0;
157 read_data_buffer_.reset();
158 read_data_loaded_data_ = "";
159 read_data_response_reader_.reset();
160
161 // Prepare for next state.
162 state_ = State::kReadInfo;
163 read_it_++; // move to next entry
164 base::ThreadTaskRunnerHandle::Get()->PostTask(
165 FROM_HERE, base::BindOnce(&AppCacheCacheTestHelper::AsyncRead,
166 base::Unretained(this), 0));
167 }
168 } break;
169 default:
170 NOTREACHED();
171 break;
172 }
173 }
174
AsyncWrite(int result)175 void AppCacheCacheTestHelper::AsyncWrite(int result) {
176 DCHECK(state_ == State::kWriteInfo || state_ == State::kWriteData);
177 DCHECK_GE(result, 0);
178 if (write_it_ == cache_entries_.end()) {
179 state_ = State::kIdle;
180 std::move(post_write_callback_).Run(1);
181 return;
182 }
183 switch (state_) {
184 case State::kWriteInfo: {
185 // Prepare for info write.
186 response_writer_.reset();
187 response_writer_ =
188 service_->storage()->CreateResponseWriter(manifest_url_);
189 AppCacheEntry* entry = cache_->GetEntry(write_it_->first);
190 if (entry) {
191 entry->add_types(write_it_->second->types);
192 entry->set_response_id(response_writer_->response_id());
193 } else {
194 cache_->AddEntry(write_it_->first,
195 AppCacheEntry(write_it_->second->types,
196 response_writer_->response_id()));
197 }
198 // Copy |response_info| so later calls can access the original response
199 // info.
200 std::unique_ptr<net::HttpResponseInfo> http_response_info =
201 std::make_unique<net::HttpResponseInfo>(
202 *(write_it_->second->response_info));
203 scoped_refptr<HttpResponseInfoIOBuffer> io_buffer =
204 base::MakeRefCounted<HttpResponseInfoIOBuffer>(
205 std::move(http_response_info));
206
207 // Prepare for next state.
208 state_ = State::kWriteData;
209
210 // Trigger async WriteInfo() call.
211 response_writer_->WriteInfo(
212 io_buffer.get(), base::BindOnce(&AppCacheCacheTestHelper::AsyncWrite,
213 base::Unretained(this)));
214 } break;
215 case State::kWriteData: {
216 // Prepare for data write.
217 std::string body = write_it_->second->body;
218 scoped_refptr<net::StringIOBuffer> io_buffer =
219 base::MakeRefCounted<net::StringIOBuffer>(body);
220
221 // Prepare for next state.
222 state_ = State::kWriteInfo;
223 write_it_++; // move to next entry
224
225 // Trigger async WriteData() call.
226 response_writer_->WriteData(
227 io_buffer.get(), body.length(),
228 base::BindOnce(&AppCacheCacheTestHelper::AsyncWrite,
229 base::Unretained(this)));
230 } break;
231 default:
232 NOTREACHED();
233 break;
234 }
235 }
236
237 } // namespace content
238