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. 4 5#include "components/component_updater/test/url_request_post_interceptor.h" 6 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/component_updater/test/test_configurator.h" 12#include "net/base/upload_bytes_element_reader.h" 13#include "net/url_request/url_request.h" 14#include "net/url_request/url_request_filter.h" 15#include "net/url_request/url_request_interceptor.h" 16#include "net/url_request/url_request_simple_job.h" 17#include "net/url_request/url_request_test_util.h" 18 19namespace component_updater { 20 21// Returns a canned response. 22class URLRequestMockJob : public net::URLRequestSimpleJob { 23 public: 24 URLRequestMockJob(net::URLRequest* request, 25 net::NetworkDelegate* network_delegate, 26 int response_code, 27 const std::string& response_body) 28 : net::URLRequestSimpleJob(request, network_delegate), 29 response_code_(response_code), 30 response_body_(response_body) {} 31 32 protected: 33 virtual int GetResponseCode() const OVERRIDE { return response_code_; } 34 35 virtual int GetData(std::string* mime_type, 36 std::string* charset, 37 std::string* data, 38 const net::CompletionCallback& callback) const OVERRIDE { 39 mime_type->assign("text/plain"); 40 charset->assign("US-ASCII"); 41 data->assign(response_body_); 42 return net::OK; 43 } 44 45 private: 46 virtual ~URLRequestMockJob() {} 47 48 int response_code_; 49 std::string response_body_; 50 DISALLOW_COPY_AND_ASSIGN(URLRequestMockJob); 51}; 52 53URLRequestPostInterceptor::URLRequestPostInterceptor( 54 const GURL& url, 55 const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) 56 : url_(url), io_task_runner_(io_task_runner), hit_count_(0) { 57} 58 59URLRequestPostInterceptor::~URLRequestPostInterceptor() { 60 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 61 ClearExpectations(); 62} 63 64void URLRequestPostInterceptor::ClearExpectations() { 65 while (!expectations_.empty()) { 66 Expectation expectation(expectations_.front()); 67 delete expectation.first; 68 expectations_.pop(); 69 } 70} 71 72GURL URLRequestPostInterceptor::GetUrl() const { 73 return url_; 74} 75 76bool URLRequestPostInterceptor::ExpectRequest( 77 class RequestMatcher* request_matcher) { 78 expectations_.push(std::make_pair(request_matcher, 79 ExpectationResponse(kResponseCode200, ""))); 80 return true; 81} 82 83bool URLRequestPostInterceptor::ExpectRequest( 84 class RequestMatcher* request_matcher, 85 int response_code) { 86 expectations_.push( 87 std::make_pair(request_matcher, ExpectationResponse(response_code, ""))); 88 return true; 89} 90 91bool URLRequestPostInterceptor::ExpectRequest( 92 class RequestMatcher* request_matcher, 93 const base::FilePath& filepath) { 94 std::string response; 95 if (filepath.empty() || !base::ReadFileToString(filepath, &response)) 96 return false; 97 98 expectations_.push(std::make_pair( 99 request_matcher, ExpectationResponse(kResponseCode200, response))); 100 return true; 101} 102 103int URLRequestPostInterceptor::GetHitCount() const { 104 base::AutoLock auto_lock(interceptor_lock_); 105 return hit_count_; 106} 107 108int URLRequestPostInterceptor::GetCount() const { 109 base::AutoLock auto_lock(interceptor_lock_); 110 return static_cast<int>(requests_.size()); 111} 112 113std::vector<std::string> URLRequestPostInterceptor::GetRequests() const { 114 base::AutoLock auto_lock(interceptor_lock_); 115 return requests_; 116} 117 118std::string URLRequestPostInterceptor::GetRequestsAsString() const { 119 std::vector<std::string> requests(GetRequests()); 120 121 std::string s = "Requests are:"; 122 123 int i = 0; 124 for (std::vector<std::string>::const_iterator it = requests.begin(); 125 it != requests.end(); 126 ++it) { 127 s.append(base::StringPrintf("\n (%d): %s", ++i, it->c_str())); 128 } 129 130 return s; 131} 132 133void URLRequestPostInterceptor::Reset() { 134 base::AutoLock auto_lock(interceptor_lock_); 135 hit_count_ = 0; 136 requests_.clear(); 137 ClearExpectations(); 138} 139 140class URLRequestPostInterceptor::Delegate : public net::URLRequestInterceptor { 141 public: 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) {} 146 147 void Register() { 148 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 149 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( 150 scheme_, hostname_, scoped_ptr<net::URLRequestInterceptor>(this)); 151 } 152 153 void Unregister() { 154 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 155 for (InterceptorMap::iterator it = interceptors_.begin(); 156 it != interceptors_.end(); 157 ++it) 158 delete (*it).second; 159 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(scheme_, 160 hostname_); 161 } 162 163 void OnCreateInterceptor(URLRequestPostInterceptor* interceptor) { 164 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 165 DCHECK(interceptors_.find(interceptor->GetUrl()) == interceptors_.end()); 166 167 interceptors_.insert(std::make_pair(interceptor->GetUrl(), interceptor)); 168 } 169 170 private: 171 virtual ~Delegate() {} 172 173 virtual net::URLRequestJob* MaybeInterceptRequest( 174 net::URLRequest* request, 175 net::NetworkDelegate* network_delegate) const OVERRIDE { 176 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 177 178 // Only intercepts POST. 179 if (!request->has_upload()) 180 return NULL; 181 182 GURL url = request->url(); 183 if (url.has_query()) { 184 GURL::Replacements replacements; 185 replacements.ClearQuery(); 186 url = url.ReplaceComponents(replacements); 187 } 188 189 InterceptorMap::const_iterator it(interceptors_.find(url)); 190 if (it == interceptors_.end()) 191 return NULL; 192 193 // There is an interceptor hooked up for this url. Read the request body, 194 // check the existing expectations, and handle the matching case by 195 // popping the expectation off the queue, counting the match, and 196 // returning a mock object to serve the canned response. 197 URLRequestPostInterceptor* interceptor(it->second); 198 199 const net::UploadDataStream* stream = request->get_upload(); 200 const net::UploadBytesElementReader* reader = 201 stream->element_readers()[0]->AsBytesReader(); 202 const int size = reader->length(); 203 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(size)); 204 const std::string request_body(reader->bytes()); 205 206 { 207 base::AutoLock auto_lock(interceptor->interceptor_lock_); 208 interceptor->requests_.push_back(request_body); 209 if (interceptor->expectations_.empty()) 210 return NULL; 211 const URLRequestPostInterceptor::Expectation& expectation( 212 interceptor->expectations_.front()); 213 if (expectation.first->Match(request_body)) { 214 const int response_code(expectation.second.response_code); 215 const std::string response_body(expectation.second.response_body); 216 delete expectation.first; 217 interceptor->expectations_.pop(); 218 ++interceptor->hit_count_; 219 220 return new URLRequestMockJob( 221 request, network_delegate, response_code, response_body); 222 } 223 } 224 225 return NULL; 226 } 227 228 typedef std::map<GURL, URLRequestPostInterceptor*> InterceptorMap; 229 InterceptorMap interceptors_; 230 231 const std::string scheme_; 232 const std::string hostname_; 233 scoped_refptr<base::SequencedTaskRunner> io_task_runner_; 234 235 DISALLOW_COPY_AND_ASSIGN(Delegate); 236}; 237 238URLRequestPostInterceptorFactory::URLRequestPostInterceptorFactory( 239 const std::string& scheme, 240 const std::string& hostname, 241 const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) 242 : scheme_(scheme), 243 hostname_(hostname), 244 io_task_runner_(io_task_runner), 245 delegate_(new URLRequestPostInterceptor::Delegate(scheme, 246 hostname, 247 io_task_runner)) { 248 io_task_runner_->PostTask( 249 FROM_HERE, 250 base::Bind(&URLRequestPostInterceptor::Delegate::Register, 251 base::Unretained(delegate_))); 252} 253 254URLRequestPostInterceptorFactory::~URLRequestPostInterceptorFactory() { 255 io_task_runner_->PostTask( 256 FROM_HERE, 257 base::Bind(&URLRequestPostInterceptor::Delegate::Unregister, 258 base::Unretained(delegate_))); 259} 260 261URLRequestPostInterceptor* URLRequestPostInterceptorFactory::CreateInterceptor( 262 const base::FilePath& filepath) { 263 const GURL base_url( 264 base::StringPrintf("%s://%s", scheme_.c_str(), hostname_.c_str())); 265 GURL absolute_url(base_url.Resolve(filepath.MaybeAsASCII())); 266 URLRequestPostInterceptor* interceptor( 267 new URLRequestPostInterceptor(absolute_url, io_task_runner_)); 268 bool res = io_task_runner_->PostTask( 269 FROM_HERE, 270 base::Bind(&URLRequestPostInterceptor::Delegate::OnCreateInterceptor, 271 base::Unretained(delegate_), 272 base::Unretained(interceptor))); 273 if (!res) { 274 delete interceptor; 275 return NULL; 276 } 277 278 return interceptor; 279} 280 281bool PartialMatch::Match(const std::string& actual) const { 282 return actual.find(expected_) != std::string::npos; 283} 284 285InterceptorFactory::InterceptorFactory( 286 const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) 287 : URLRequestPostInterceptorFactory(POST_INTERCEPT_SCHEME, 288 POST_INTERCEPT_HOSTNAME, 289 io_task_runner) { 290} 291 292InterceptorFactory::~InterceptorFactory() { 293} 294 295URLRequestPostInterceptor* InterceptorFactory::CreateInterceptor() { 296 return CreateInterceptorForPath(POST_INTERCEPT_PATH); 297} 298 299URLRequestPostInterceptor* InterceptorFactory::CreateInterceptorForPath( 300 const char* url_path) { 301 return URLRequestPostInterceptorFactory::CreateInterceptor( 302 base::FilePath::FromUTF8Unsafe(url_path)); 303} 304 305} // namespace component_updater 306