1 // Copyright 2018 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 "chrome/chrome_cleaner/http/mock_http_agent_factory.h"
6 
7 #include <stdint.h>
8 
9 #include <algorithm>
10 #include <memory>
11 #include <string>
12 #include <vector>
13 
14 #include "base/check.h"
15 #include "chrome/chrome_cleaner/http/http_agent.h"
16 #include "chrome/chrome_cleaner/http/http_agent_factory.h"
17 #include "chrome/chrome_cleaner/http/http_response.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace chrome_cleaner {
21 
22 namespace {
23 
24 // Class that provides a response based on how the MockHttpAgentConfig is
25 // configured.
26 class MockHttpResponse : public chrome_cleaner::HttpResponse {
27  public:
MockHttpResponse(MockHttpAgentConfig * config)28   explicit MockHttpResponse(MockHttpAgentConfig* config) : config_(config) {
29     DCHECK(config);
30   }
31 
32   ~MockHttpResponse() override = default;
33 
34   // chrome_cleaner::HttpResponse:
GetStatusCode(uint16_t * status_code)35   bool GetStatusCode(uint16_t* status_code) override {
36     if (config_->GetCurrentCalls().get_status_code_succeeds) {
37       *status_code = static_cast<uint16_t>(
38           config_->GetCurrentCalls().get_status_code_result);
39     }
40     return config_->GetCurrentCalls().get_status_code_succeeds;
41   }
42 
GetContentLength(bool * has_content_length,uint32_t * content_length)43   bool GetContentLength(bool* has_content_length,
44                         uint32_t* content_length) override {
45     ADD_FAILURE() << "This method should not be called.";
46     return false;
47   }
48 
GetContentType(bool * has_content_type,std::wstring * content_type)49   bool GetContentType(bool* has_content_type,
50                       std::wstring* content_type) override {
51     ADD_FAILURE() << "This method should not be called.";
52     return false;
53   }
54 
HasData(bool * has_data)55   bool HasData(bool* has_data) override {
56     if (config_->GetCurrentCalls().has_data_succeeds)
57       *has_data = !config_->GetCurrentCalls().read_data_result.empty();
58     return config_->GetCurrentCalls().has_data_succeeds;
59   }
60 
ReadData(char * buffer,uint32_t * count)61   bool ReadData(char* buffer, uint32_t* count) override {
62     MockHttpAgentConfig::Calls& calls = config_->GetCurrentCalls();
63 
64     bool succeeds = calls.read_data_succeeds_by_default;
65     if (!calls.read_data_success_sequence.empty()) {
66       succeeds = calls.read_data_success_sequence[0];
67       calls.read_data_success_sequence.erase(
68           calls.read_data_success_sequence.begin());
69     }
70 
71     if (succeeds)
72       config_->ReadData(buffer, count);
73     return succeeds;
74   }
75 
76  private:
77   MockHttpAgentConfig* config_{nullptr};
78 
79   DISALLOW_COPY_AND_ASSIGN(MockHttpResponse);
80 };
81 
82 // Class that acts as an HttpAgent based on how the MockHttpAgentConfig is
83 // configured.
84 class MockHttpAgent : public chrome_cleaner::HttpAgent {
85  public:
MockHttpAgent(MockHttpAgentConfig * config)86   explicit MockHttpAgent(MockHttpAgentConfig* config) : config_(config) {
87     DCHECK(config);
88   }
89 
90   ~MockHttpAgent() override = default;
91 
92   // chrome_cleaner::HttpAgent:
Post(const std::wstring & host,uint16_t port,const std::wstring & path,bool secure,const std::wstring & extra_headers,const std::string & body,const net::NetworkTrafficAnnotationTag &)93   std::unique_ptr<chrome_cleaner::HttpResponse> Post(
94       const std::wstring& host,
95       uint16_t port,
96       const std::wstring& path,
97       bool secure,
98       const std::wstring& extra_headers,
99       const std::string& body,
100       const net::NetworkTrafficAnnotationTag& /*traffic_annotation*/) override {
101     const bool post_succeeds = config_->GetCurrentCalls().request_succeeds;
102     MockHttpAgentConfig::RequestData post_data;
103     post_data.host = host;
104     post_data.port = port;
105     post_data.path = path;
106     post_data.secure = secure;
107     post_data.extra_headers = extra_headers;
108     post_data.body = body;
109     config_->AddRequestData(post_data);
110 
111     if (post_succeeds)
112       return std::make_unique<MockHttpResponse>(config_);
113     return std::unique_ptr<MockHttpResponse>();
114   }
115 
116   // chrome_cleaner::HttpAgent:
Get(const std::wstring & host,uint16_t port,const std::wstring & path,bool secure,const std::wstring & extra_headers,const net::NetworkTrafficAnnotationTag &)117   std::unique_ptr<chrome_cleaner::HttpResponse> Get(
118       const std::wstring& host,
119       uint16_t port,
120       const std::wstring& path,
121       bool secure,
122       const std::wstring& extra_headers,
123       const net::NetworkTrafficAnnotationTag& /*traffic_annotation*/) override {
124     const bool get_succeeds = config_->GetCurrentCalls().request_succeeds;
125     MockHttpAgentConfig::RequestData get_data;
126     get_data.host = host;
127     get_data.port = port;
128     get_data.path = path;
129     get_data.secure = secure;
130     get_data.extra_headers = extra_headers;
131     config_->AddRequestData(get_data);
132 
133     if (get_succeeds)
134       return std::make_unique<MockHttpResponse>(config_);
135     return std::unique_ptr<MockHttpResponse>();
136   }
137 
138  private:
139   MockHttpAgentConfig* config_{nullptr};
140 
141   DISALLOW_COPY_AND_ASSIGN(MockHttpAgent);
142 };
143 
144 }  // namespace
145 
Calls(HttpStatus status)146 MockHttpAgentConfig::Calls::Calls(HttpStatus status)
147     : get_status_code_result(status) {}
148 
149 MockHttpAgentConfig::Calls::Calls(const Calls& other) = default;
150 
151 MockHttpAgentConfig::Calls::~Calls() = default;
152 
153 MockHttpAgentConfig::Calls& MockHttpAgentConfig::Calls::operator=(
154     const MockHttpAgentConfig::Calls& other) = default;
155 
156 MockHttpAgentConfig::RequestData::RequestData() = default;
157 
158 MockHttpAgentConfig::RequestData::RequestData(const RequestData& other) =
159     default;
160 
161 MockHttpAgentConfig::RequestData::~RequestData() = default;
162 
163 MockHttpAgentConfig::RequestData& MockHttpAgentConfig::RequestData::operator=(
164     const MockHttpAgentConfig::RequestData& other) = default;
165 
166 MockHttpAgentConfig::MockHttpAgentConfig() = default;
167 
168 MockHttpAgentConfig::~MockHttpAgentConfig() = default;
169 
AddCalls(const Calls & calls)170 size_t MockHttpAgentConfig::AddCalls(const Calls& calls) {
171   calls_.push_back(calls);
172   return calls_.size() - 1;
173 }
174 
GetCurrentCalls()175 MockHttpAgentConfig::Calls& MockHttpAgentConfig::GetCurrentCalls() {
176   if (current_index_ >= calls_.size()) {
177     static Calls default_calls(HttpStatus::kOk);
178     ADD_FAILURE() << "Did not expect more than " << calls_.size() << " tries";
179     return default_calls;
180   }
181   return calls_[current_index_];
182 }
183 
ReadData(char * buffer,uint32_t * count)184 void MockHttpAgentConfig::ReadData(char* buffer, uint32_t* count) {
185   if (current_index_ >= calls_.size()) {
186     ADD_FAILURE() << "Reading data for an unexpected call";
187     *count = 0;
188     return;
189   }
190   Calls& calls = calls_[current_index_];
191   *count =
192       std::min(*count, static_cast<uint32_t>(calls.read_data_result.size()));
193   memcpy(buffer, calls.read_data_result.c_str(), *count);
194   calls.read_data_result = calls.read_data_result.substr(*count);
195 }
196 
AddRequestData(const RequestData & request_data)197 void MockHttpAgentConfig::AddRequestData(const RequestData& request_data) {
198   ASSERT_EQ(request_data_.size(), current_index_)
199       << "MockHttpAgentConfig does not support creating multiple agents "
200       << "without calling Post or Get on each before creating the next one. "
201       << "Suggest adding support to MockHttpAgentConfig for that if necessary, "
202       << "or updating your code to avoid this.";
203   request_data_.push_back(request_data);
204 }
205 
MockHttpAgentFactory(MockHttpAgentConfig * config)206 MockHttpAgentFactory::MockHttpAgentFactory(MockHttpAgentConfig* config)
207     : config_(config) {
208   DCHECK(config);
209 }
210 
211 std::unique_ptr<chrome_cleaner::HttpAgent>
CreateHttpAgent() const212 MockHttpAgentFactory::CreateHttpAgent() const {
213   // Set the configuration index to the next one (one per HttpAgent).
214   if (config_->current_index_ == MockHttpAgentConfig::kInvalidIndex)
215     config_->current_index_ = 0;
216   else
217     ++config_->current_index_;
218 
219   return std::make_unique<MockHttpAgent>(config_);
220 }
221 
222 }  // namespace chrome_cleaner
223