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/component_updater/test/url_request_post_interceptor.h"
7 #include "base/file_util.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/strings/stringprintf.h"
10 #include "components/component_updater/test/test_configurator.h"
11 #include "net/base/upload_bytes_element_reader.h"
12 #include "net/url_request/url_request.h"
13 #include "net/url_request/url_request_filter.h"
14 #include "net/url_request/url_request_interceptor.h"
15 #include "net/url_request/url_request_simple_job.h"
16 #include "net/url_request/url_request_test_util.h"
18 namespace component_updater
{
20 // Returns a canned response.
21 class URLRequestMockJob
: public net::URLRequestSimpleJob
{
23 URLRequestMockJob(net::URLRequest
* request
,
24 net::NetworkDelegate
* network_delegate
,
25 const std::string
& response
)
26 : net::URLRequestSimpleJob(request
, network_delegate
),
27 response_(response
) {}
30 virtual int GetResponseCode() const OVERRIDE
{ return 200; }
32 virtual int GetData(std::string
* mime_type
,
35 const net::CompletionCallback
& callback
) const OVERRIDE
{
36 mime_type
->assign("text/plain");
37 charset
->assign("US-ASCII");
38 data
->assign(response_
);
43 virtual ~URLRequestMockJob() {}
45 std::string response_
;
46 DISALLOW_COPY_AND_ASSIGN(URLRequestMockJob
);
49 URLRequestPostInterceptor::URLRequestPostInterceptor(
51 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
)
52 : url_(url
), io_task_runner_(io_task_runner
), hit_count_(0) {
55 URLRequestPostInterceptor::~URLRequestPostInterceptor() {
56 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
60 void URLRequestPostInterceptor::ClearExpectations() {
61 while (!expectations_
.empty()) {
62 Expectation
expectation(expectations_
.front());
63 delete expectation
.first
;
68 GURL
URLRequestPostInterceptor::GetUrl() const {
72 bool URLRequestPostInterceptor::ExpectRequest(
73 class RequestMatcher
* request_matcher
) {
74 expectations_
.push(std::make_pair(request_matcher
, ""));
78 bool URLRequestPostInterceptor::ExpectRequest(
79 class RequestMatcher
* request_matcher
,
80 const base::FilePath
& filepath
) {
82 if (filepath
.empty() || !base::ReadFileToString(filepath
, &response
))
84 expectations_
.push(std::make_pair(request_matcher
, response
));
88 int URLRequestPostInterceptor::GetHitCount() const {
89 base::AutoLock
auto_lock(interceptor_lock_
);
93 int URLRequestPostInterceptor::GetCount() const {
94 base::AutoLock
auto_lock(interceptor_lock_
);
95 return static_cast<int>(requests_
.size());
98 std::vector
<std::string
> URLRequestPostInterceptor::GetRequests() const {
99 base::AutoLock
auto_lock(interceptor_lock_
);
103 std::string
URLRequestPostInterceptor::GetRequestsAsString() const {
104 std::vector
<std::string
> requests(GetRequests());
106 std::string s
= "Requests are:";
109 for (std::vector
<std::string
>::const_iterator it
= requests
.begin();
110 it
!= requests
.end();
112 s
.append(base::StringPrintf("\n (%d): %s", ++i
, it
->c_str()));
118 void URLRequestPostInterceptor::Reset() {
119 base::AutoLock
auto_lock(interceptor_lock_
);
125 class URLRequestPostInterceptor::Delegate
: public net::URLRequestInterceptor
{
127 Delegate(const std::string
& scheme
,
128 const std::string
& hostname
,
129 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
)
130 : scheme_(scheme
), hostname_(hostname
), io_task_runner_(io_task_runner
) {}
133 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
134 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
135 scheme_
, hostname_
, scoped_ptr
<net::URLRequestInterceptor
>(this));
139 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
140 for (InterceptorMap::iterator it
= interceptors_
.begin();
141 it
!= interceptors_
.end();
144 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(scheme_
,
148 void OnCreateInterceptor(URLRequestPostInterceptor
* interceptor
) {
149 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
150 CHECK(interceptors_
.find(interceptor
->GetUrl()) == interceptors_
.end());
152 interceptors_
.insert(std::make_pair(interceptor
->GetUrl(), interceptor
));
156 virtual ~Delegate() {}
158 virtual net::URLRequestJob
* MaybeInterceptRequest(
159 net::URLRequest
* request
,
160 net::NetworkDelegate
* network_delegate
) const OVERRIDE
{
161 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
163 // Only intercepts POST.
164 if (!request
->has_upload())
167 GURL url
= request
->url();
168 if (url
.has_query()) {
169 GURL::Replacements replacements
;
170 replacements
.ClearQuery();
171 url
= url
.ReplaceComponents(replacements
);
174 InterceptorMap::const_iterator
it(interceptors_
.find(url
));
175 if (it
== interceptors_
.end())
178 // There is an interceptor hooked up for this url. Read the request body,
179 // check the existing expectations, and handle the matching case by
180 // popping the expectation off the queue, counting the match, and
181 // returning a mock object to serve the canned response.
182 URLRequestPostInterceptor
* interceptor(it
->second
);
184 const net::UploadDataStream
* stream
= request
->get_upload();
185 const net::UploadBytesElementReader
* reader
=
186 stream
->element_readers()[0]->AsBytesReader();
187 const int size
= reader
->length();
188 scoped_refptr
<net::IOBuffer
> buffer(new net::IOBuffer(size
));
189 const std::string
request_body(reader
->bytes());
192 base::AutoLock
auto_lock(interceptor
->interceptor_lock_
);
193 interceptor
->requests_
.push_back(request_body
);
194 if (interceptor
->expectations_
.empty())
196 const URLRequestPostInterceptor::Expectation
& expectation(
197 interceptor
->expectations_
.front());
198 if (expectation
.first
->Match(request_body
)) {
199 const std::string
response(expectation
.second
);
200 delete expectation
.first
;
201 interceptor
->expectations_
.pop();
202 ++interceptor
->hit_count_
;
204 return new URLRequestMockJob(request
, network_delegate
, response
);
211 typedef std::map
<GURL
, URLRequestPostInterceptor
*> InterceptorMap
;
212 InterceptorMap interceptors_
;
214 const std::string scheme_
;
215 const std::string hostname_
;
216 scoped_refptr
<base::SequencedTaskRunner
> io_task_runner_
;
218 DISALLOW_COPY_AND_ASSIGN(Delegate
);
221 URLRequestPostInterceptorFactory::URLRequestPostInterceptorFactory(
222 const std::string
& scheme
,
223 const std::string
& hostname
,
224 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
)
227 io_task_runner_(io_task_runner
),
228 delegate_(new URLRequestPostInterceptor::Delegate(scheme
,
231 io_task_runner_
->PostTask(
233 base::Bind(&URLRequestPostInterceptor::Delegate::Register
,
234 base::Unretained(delegate_
)));
237 URLRequestPostInterceptorFactory::~URLRequestPostInterceptorFactory() {
238 io_task_runner_
->PostTask(
240 base::Bind(&URLRequestPostInterceptor::Delegate::Unregister
,
241 base::Unretained(delegate_
)));
244 URLRequestPostInterceptor
* URLRequestPostInterceptorFactory::CreateInterceptor(
245 const base::FilePath
& filepath
) {
247 base::StringPrintf("%s://%s", scheme_
.c_str(), hostname_
.c_str()));
248 GURL
absolute_url(base_url
.Resolve(filepath
.MaybeAsASCII()));
249 URLRequestPostInterceptor
* interceptor(
250 new URLRequestPostInterceptor(absolute_url
, io_task_runner_
));
251 bool res
= io_task_runner_
->PostTask(
253 base::Bind(&URLRequestPostInterceptor::Delegate::OnCreateInterceptor
,
254 base::Unretained(delegate_
),
255 base::Unretained(interceptor
)));
264 bool PartialMatch::Match(const std::string
& actual
) const {
265 return actual
.find(expected_
) != std::string::npos
;
268 InterceptorFactory::InterceptorFactory(
269 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
)
270 : URLRequestPostInterceptorFactory(POST_INTERCEPT_SCHEME
,
271 POST_INTERCEPT_HOSTNAME
,
275 InterceptorFactory::~InterceptorFactory() {
278 URLRequestPostInterceptor
* InterceptorFactory::CreateInterceptor() {
279 return URLRequestPostInterceptorFactory::CreateInterceptor(
280 base::FilePath::FromUTF8Unsafe(POST_INTERCEPT_PATH
));
283 } // namespace component_updater