Home | History | Annotate | Download | only in test
      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 "chrome/browser/component_updater/test/url_request_post_interceptor.h"
      6 #include "base/file_util.h"
      7 #include "base/memory/scoped_ptr.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "content/public/test/test_browser_thread.h"
     10 #include "net/base/upload_bytes_element_reader.h"
     11 #include "net/url_request/url_request.h"
     12 #include "net/url_request/url_request_filter.h"
     13 #include "net/url_request/url_request_job_factory.h"
     14 #include "net/url_request/url_request_simple_job.h"
     15 #include "net/url_request/url_request_test_util.h"
     16 
     17 using content::BrowserThread;
     18 
     19 namespace component_updater {
     20 
     21 // Returns a canned response.
     22 class URLRequestMockJob : public net::URLRequestSimpleJob {
     23  public:
     24   URLRequestMockJob(net::URLRequest* request,
     25                     net::NetworkDelegate* network_delegate,
     26                     const std::string& response)
     27       : net::URLRequestSimpleJob(request, network_delegate),
     28         response_(response) {}
     29 
     30  protected:
     31   virtual int GetResponseCode() const OVERRIDE {
     32     return 200;
     33   }
     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_);
     42     return net::OK;
     43   }
     44 
     45  private:
     46   virtual ~URLRequestMockJob() {}
     47 
     48   std::string response_;
     49   DISALLOW_COPY_AND_ASSIGN(URLRequestMockJob);
     50 };
     51 
     52 URLRequestPostInterceptor::URLRequestPostInterceptor(const GURL& url)
     53     : url_(url), hit_count_(0) {}
     54 
     55 URLRequestPostInterceptor::~URLRequestPostInterceptor() {
     56   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     57   ClearExpectations();
     58 }
     59 
     60 void URLRequestPostInterceptor::ClearExpectations() {
     61   while (!expectations_.empty()) {
     62     Expectation expectation(expectations_.front());
     63     delete expectation.first;
     64     expectations_.pop();
     65   }
     66 }
     67 
     68 GURL URLRequestPostInterceptor::GetUrl() const {
     69   return url_;
     70 }
     71 
     72 bool URLRequestPostInterceptor::ExpectRequest(
     73     class RequestMatcher* request_matcher) {
     74   expectations_.push(std::make_pair(request_matcher, ""));
     75   return true;
     76 }
     77 
     78 bool URLRequestPostInterceptor::ExpectRequest(
     79     class RequestMatcher* request_matcher,
     80     const base::FilePath& filepath) {
     81   std::string response;
     82   if (filepath.empty() || !base::ReadFileToString(filepath, &response))
     83     return false;
     84   expectations_.push(std::make_pair(request_matcher, response));
     85   return true;
     86 }
     87 
     88 int URLRequestPostInterceptor::GetHitCount() const {
     89   base::AutoLock auto_lock(interceptor_lock_);
     90   return hit_count_;
     91 }
     92 
     93 int URLRequestPostInterceptor::GetCount() const {
     94   base::AutoLock auto_lock(interceptor_lock_);
     95   return static_cast<int>(requests_.size());
     96 }
     97 
     98 std::vector<std::string>
     99 URLRequestPostInterceptor::GetRequests() const {
    100  base::AutoLock auto_lock(interceptor_lock_);
    101  return requests_;
    102 }
    103 
    104 std::string URLRequestPostInterceptor::GetRequestsAsString() const {
    105   std::vector<std::string> requests(GetRequests());
    106 
    107   std::string s = "Requests are:";
    108 
    109   int i = 0;
    110   for (std::vector<std::string>::const_iterator it = requests.begin();
    111       it != requests.end(); ++it) {
    112     s.append(base::StringPrintf("\n  (%d): %s", ++i, it->c_str()));
    113   }
    114 
    115   return s;
    116 }
    117 
    118 void URLRequestPostInterceptor::Reset() {
    119   base::AutoLock auto_lock(interceptor_lock_);
    120   hit_count_ = 0;
    121   requests_.clear();
    122   ClearExpectations();
    123 }
    124 
    125 
    126 class URLRequestPostInterceptor::Delegate
    127     : public net::URLRequestJobFactory::ProtocolHandler {
    128  public:
    129   Delegate(const std::string& scheme, const std::string& hostname)
    130       : scheme_(scheme), hostname_(hostname) {}
    131 
    132   void Register() {
    133     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    134     net::URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
    135         scheme_, hostname_,
    136         scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(this));
    137   }
    138 
    139   void Unregister() {
    140     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    141     for (InterceptorMap::iterator it = interceptors_.begin();
    142         it != interceptors_.end(); ++it)
    143       delete (*it).second;
    144     net::URLRequestFilter::GetInstance()->
    145       RemoveHostnameHandler(scheme_, hostname_);
    146   }
    147 
    148   void OnCreateInterceptor(URLRequestPostInterceptor* interceptor) {
    149     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    150     CHECK(interceptors_.find(interceptor->GetUrl()) == interceptors_.end());
    151 
    152     interceptors_.insert(std::make_pair(interceptor->GetUrl(), interceptor));
    153   }
    154 
    155  private:
    156   virtual ~Delegate() {}
    157 
    158   virtual net::URLRequestJob* MaybeCreateJob(
    159       net::URLRequest* request,
    160       net::NetworkDelegate* network_delegate) const OVERRIDE {
    161     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    162 
    163     // Only intercepts POST.
    164     if (!request->has_upload())
    165       return NULL;
    166 
    167     GURL url = request->url();
    168     if (url.has_query()) {
    169       GURL::Replacements replacements;
    170       replacements.ClearQuery();
    171       url = url.ReplaceComponents(replacements);
    172     }
    173 
    174     InterceptorMap::const_iterator it(interceptors_.find(url));
    175     if (it == interceptors_.end())
    176       return NULL;
    177 
    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);
    183 
    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());
    190 
    191     {
    192       base::AutoLock auto_lock(interceptor->interceptor_lock_);
    193       interceptor->requests_.push_back(request_body);
    194       if (interceptor->expectations_.empty())
    195         return NULL;
    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_;
    203 
    204         return new URLRequestMockJob(request, network_delegate, response);
    205       }
    206     }
    207 
    208     return NULL;
    209   }
    210 
    211   typedef std::map<GURL, URLRequestPostInterceptor*> InterceptorMap;
    212   InterceptorMap interceptors_;
    213 
    214   const std::string scheme_;
    215   const std::string hostname_;
    216 
    217   DISALLOW_COPY_AND_ASSIGN(Delegate);
    218 };
    219 
    220 URLRequestPostInterceptorFactory::URLRequestPostInterceptorFactory(
    221     const std::string& scheme,
    222     const std::string& hostname)
    223     : scheme_(scheme),
    224       hostname_(hostname),
    225       delegate_(new URLRequestPostInterceptor::Delegate(scheme, hostname)) {
    226   BrowserThread::PostTask(
    227       BrowserThread::IO, FROM_HERE,
    228       base::Bind(&URLRequestPostInterceptor::Delegate::Register,
    229                  base::Unretained(delegate_)));
    230 }
    231 
    232 URLRequestPostInterceptorFactory::~URLRequestPostInterceptorFactory() {
    233   BrowserThread::PostTask(
    234       BrowserThread::IO, FROM_HERE,
    235       base::Bind(&URLRequestPostInterceptor::Delegate::Unregister,
    236                  base::Unretained(delegate_)));
    237 }
    238 
    239 URLRequestPostInterceptor* URLRequestPostInterceptorFactory::CreateInterceptor(
    240     const base::FilePath& filepath) {
    241   const GURL base_url(base::StringPrintf("%s://%s",
    242                                          scheme_.c_str(),
    243                                          hostname_.c_str()));
    244   GURL absolute_url(base_url.Resolve(filepath.MaybeAsASCII()));
    245   URLRequestPostInterceptor* interceptor(
    246       new URLRequestPostInterceptor(absolute_url));
    247   bool res = BrowserThread::PostTask(
    248       BrowserThread::IO, FROM_HERE,
    249       base::Bind(&URLRequestPostInterceptor::Delegate::OnCreateInterceptor,
    250                  base::Unretained(delegate_),
    251                  base::Unretained(interceptor)));
    252   if (!res) {
    253     delete interceptor;
    254     return NULL;
    255   }
    256 
    257   return interceptor;
    258 }
    259 
    260 }  // namespace component_updater
    261 
    262