1 // Copyright 2013 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.
5 #include "components/update_client/url_request_post_interceptor.h"
7 #include "base/files/file_util.h"
8 #include "base/macros.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/stringprintf.h"
11 #include "components/update_client/test_configurator.h"
12 #include "net/base/upload_bytes_element_reader.h"
13 #include "net/base/upload_data_stream.h"
14 #include "net/url_request/url_request.h"
15 #include "net/url_request/url_request_filter.h"
16 #include "net/url_request/url_request_interceptor.h"
17 #include "net/url_request/url_request_simple_job.h"
18 #include "net/url_request/url_request_test_util.h"
20 namespace update_client
{
22 // Returns a canned response.
23 class URLRequestMockJob
: public net::URLRequestSimpleJob
{
25 URLRequestMockJob(net::URLRequest
* request
,
26 net::NetworkDelegate
* network_delegate
,
28 const std::string
& response_body
)
29 : net::URLRequestSimpleJob(request
, network_delegate
),
30 response_code_(response_code
),
31 response_body_(response_body
) {}
34 int GetResponseCode() const override
{ return response_code_
; }
36 int GetData(std::string
* mime_type
,
39 const net::CompletionCallback
& callback
) const override
{
40 mime_type
->assign("text/plain");
41 charset
->assign("US-ASCII");
42 data
->assign(response_body_
);
47 ~URLRequestMockJob() override
{}
50 std::string response_body_
;
51 DISALLOW_COPY_AND_ASSIGN(URLRequestMockJob
);
54 URLRequestPostInterceptor::URLRequestPostInterceptor(
56 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
)
57 : url_(url
), io_task_runner_(io_task_runner
), hit_count_(0) {
60 URLRequestPostInterceptor::~URLRequestPostInterceptor() {
61 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
65 void URLRequestPostInterceptor::ClearExpectations() {
66 while (!expectations_
.empty()) {
67 Expectation
expectation(expectations_
.front());
68 delete expectation
.first
;
73 GURL
URLRequestPostInterceptor::GetUrl() const {
77 bool URLRequestPostInterceptor::ExpectRequest(
78 class RequestMatcher
* request_matcher
) {
79 expectations_
.push(std::make_pair(request_matcher
,
80 ExpectationResponse(kResponseCode200
, "")));
84 bool URLRequestPostInterceptor::ExpectRequest(
85 class RequestMatcher
* request_matcher
,
88 std::make_pair(request_matcher
, ExpectationResponse(response_code
, "")));
92 bool URLRequestPostInterceptor::ExpectRequest(
93 class RequestMatcher
* request_matcher
,
94 const base::FilePath
& filepath
) {
96 if (filepath
.empty() || !base::ReadFileToString(filepath
, &response
))
99 expectations_
.push(std::make_pair(
100 request_matcher
, ExpectationResponse(kResponseCode200
, response
)));
104 int URLRequestPostInterceptor::GetHitCount() const {
105 base::AutoLock
auto_lock(interceptor_lock_
);
109 int URLRequestPostInterceptor::GetCount() const {
110 base::AutoLock
auto_lock(interceptor_lock_
);
111 return static_cast<int>(requests_
.size());
114 std::vector
<std::string
> URLRequestPostInterceptor::GetRequests() const {
115 base::AutoLock
auto_lock(interceptor_lock_
);
119 std::string
URLRequestPostInterceptor::GetRequestsAsString() const {
120 std::vector
<std::string
> requests(GetRequests());
122 std::string s
= "Requests are:";
125 for (std::vector
<std::string
>::const_iterator it
= requests
.begin();
126 it
!= requests
.end(); ++it
) {
127 s
.append(base::StringPrintf("\n (%d): %s", ++i
, it
->c_str()));
133 void URLRequestPostInterceptor::Reset() {
134 base::AutoLock
auto_lock(interceptor_lock_
);
140 class URLRequestPostInterceptor::Delegate
: public net::URLRequestInterceptor
{
142 Delegate(const std::string
& scheme
,
143 const std::string
& hostname
,
144 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
)
145 : scheme_(scheme
), hostname_(hostname
), io_task_runner_(io_task_runner
) {}
148 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
149 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
150 scheme_
, hostname_
, scoped_ptr
<net::URLRequestInterceptor
>(this));
154 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
155 for (InterceptorMap::iterator it
= interceptors_
.begin();
156 it
!= interceptors_
.end(); ++it
)
158 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(scheme_
,
162 void OnCreateInterceptor(URLRequestPostInterceptor
* interceptor
) {
163 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
164 DCHECK(interceptors_
.find(interceptor
->GetUrl()) == interceptors_
.end());
166 interceptors_
.insert(std::make_pair(interceptor
->GetUrl(), interceptor
));
170 ~Delegate() override
{}
172 net::URLRequestJob
* MaybeInterceptRequest(
173 net::URLRequest
* request
,
174 net::NetworkDelegate
* network_delegate
) const override
{
175 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
177 // Only intercepts POST.
178 if (!request
->has_upload())
181 GURL url
= request
->url();
182 if (url
.has_query()) {
183 GURL::Replacements replacements
;
184 replacements
.ClearQuery();
185 url
= url
.ReplaceComponents(replacements
);
188 InterceptorMap::const_iterator
it(interceptors_
.find(url
));
189 if (it
== interceptors_
.end())
192 // There is an interceptor hooked up for this url. Read the request body,
193 // check the existing expectations, and handle the matching case by
194 // popping the expectation off the queue, counting the match, and
195 // returning a mock object to serve the canned response.
196 URLRequestPostInterceptor
* interceptor(it
->second
);
198 const net::UploadDataStream
* stream
= request
->get_upload();
199 const net::UploadBytesElementReader
* reader
=
200 (*stream
->GetElementReaders())[0]->AsBytesReader();
201 const int size
= reader
->length();
202 scoped_refptr
<net::IOBuffer
> buffer(new net::IOBuffer(size
));
203 const std::string
request_body(reader
->bytes());
206 base::AutoLock
auto_lock(interceptor
->interceptor_lock_
);
207 interceptor
->requests_
.push_back(request_body
);
208 if (interceptor
->expectations_
.empty())
210 const URLRequestPostInterceptor::Expectation
& expectation(
211 interceptor
->expectations_
.front());
212 if (expectation
.first
->Match(request_body
)) {
213 const int response_code(expectation
.second
.response_code
);
214 const std::string
response_body(expectation
.second
.response_body
);
215 delete expectation
.first
;
216 interceptor
->expectations_
.pop();
217 ++interceptor
->hit_count_
;
219 return new URLRequestMockJob(request
, network_delegate
, response_code
,
227 typedef std::map
<GURL
, URLRequestPostInterceptor
*> InterceptorMap
;
228 InterceptorMap interceptors_
;
230 const std::string scheme_
;
231 const std::string hostname_
;
232 scoped_refptr
<base::SequencedTaskRunner
> io_task_runner_
;
234 DISALLOW_COPY_AND_ASSIGN(Delegate
);
237 URLRequestPostInterceptorFactory::URLRequestPostInterceptorFactory(
238 const std::string
& scheme
,
239 const std::string
& hostname
,
240 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
)
243 io_task_runner_(io_task_runner
),
244 delegate_(new URLRequestPostInterceptor::Delegate(scheme
,
247 io_task_runner_
->PostTask(
248 FROM_HERE
, base::Bind(&URLRequestPostInterceptor::Delegate::Register
,
249 base::Unretained(delegate_
)));
252 URLRequestPostInterceptorFactory::~URLRequestPostInterceptorFactory() {
253 io_task_runner_
->PostTask(
254 FROM_HERE
, base::Bind(&URLRequestPostInterceptor::Delegate::Unregister
,
255 base::Unretained(delegate_
)));
258 URLRequestPostInterceptor
* URLRequestPostInterceptorFactory::CreateInterceptor(
259 const base::FilePath
& filepath
) {
261 base::StringPrintf("%s://%s", scheme_
.c_str(), hostname_
.c_str()));
262 GURL
absolute_url(base_url
.Resolve(filepath
.MaybeAsASCII()));
263 URLRequestPostInterceptor
* interceptor(
264 new URLRequestPostInterceptor(absolute_url
, io_task_runner_
));
265 bool res
= io_task_runner_
->PostTask(
267 base::Bind(&URLRequestPostInterceptor::Delegate::OnCreateInterceptor
,
268 base::Unretained(delegate_
), base::Unretained(interceptor
)));
277 bool PartialMatch::Match(const std::string
& actual
) const {
278 return actual
.find(expected_
) != std::string::npos
;
281 InterceptorFactory::InterceptorFactory(
282 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
)
283 : URLRequestPostInterceptorFactory(POST_INTERCEPT_SCHEME
,
284 POST_INTERCEPT_HOSTNAME
,
288 InterceptorFactory::~InterceptorFactory() {
291 URLRequestPostInterceptor
* InterceptorFactory::CreateInterceptor() {
292 return CreateInterceptorForPath(POST_INTERCEPT_PATH
);
295 URLRequestPostInterceptor
* InterceptorFactory::CreateInterceptorForPath(
296 const char* url_path
) {
297 return URLRequestPostInterceptorFactory::CreateInterceptor(
298 base::FilePath::FromUTF8Unsafe(url_path
));
301 } // namespace update_client