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