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_interceptor.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 { return 200; }
     32 
     33   virtual int GetData(std::string* mime_type,
     34                       std::string* charset,
     35                       std::string* data,
     36                       const net::CompletionCallback& callback) const OVERRIDE {
     37     mime_type->assign("text/plain");
     38     charset->assign("US-ASCII");
     39     data->assign(response_);
     40     return net::OK;
     41   }
     42 
     43  private:
     44   virtual ~URLRequestMockJob() {}
     45 
     46   std::string response_;
     47   DISALLOW_COPY_AND_ASSIGN(URLRequestMockJob);
     48 };
     49 
     50 URLRequestPostInterceptor::URLRequestPostInterceptor(const GURL& url)
     51     : url_(url), hit_count_(0) {
     52 }
     53 
     54 URLRequestPostInterceptor::~URLRequestPostInterceptor() {
     55   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     56   ClearExpectations();
     57 }
     58 
     59 void URLRequestPostInterceptor::ClearExpectations() {
     60   while (!expectations_.empty()) {
     61     Expectation expectation(expectations_.front());
     62     delete expectation.first;
     63     expectations_.pop();
     64   }
     65 }
     66 
     67 GURL URLRequestPostInterceptor::GetUrl() const {
     68   return url_;
     69 }
     70 
     71 bool URLRequestPostInterceptor::ExpectRequest(
     72     class RequestMatcher* request_matcher) {
     73   expectations_.push(std::make_pair(request_matcher, ""));
     74   return true;
     75 }
     76 
     77 bool URLRequestPostInterceptor::ExpectRequest(
     78     class RequestMatcher* request_matcher,
     79     const base::FilePath& filepath) {
     80   std::string response;
     81   if (filepath.empty() || !base::ReadFileToString(filepath, &response))
     82     return false;
     83   expectations_.push(std::make_pair(request_matcher, response));
     84   return true;
     85 }
     86 
     87 int URLRequestPostInterceptor::GetHitCount() const {
     88   base::AutoLock auto_lock(interceptor_lock_);
     89   return hit_count_;
     90 }
     91 
     92 int URLRequestPostInterceptor::GetCount() const {
     93   base::AutoLock auto_lock(interceptor_lock_);
     94   return static_cast<int>(requests_.size());
     95 }
     96 
     97 std::vector<std::string> URLRequestPostInterceptor::GetRequests() const {
     98   base::AutoLock auto_lock(interceptor_lock_);
     99   return requests_;
    100 }
    101 
    102 std::string URLRequestPostInterceptor::GetRequestsAsString() const {
    103   std::vector<std::string> requests(GetRequests());
    104 
    105   std::string s = "Requests are:";
    106 
    107   int i = 0;
    108   for (std::vector<std::string>::const_iterator it = requests.begin();
    109        it != requests.end();
    110        ++it) {
    111     s.append(base::StringPrintf("\n  (%d): %s", ++i, it->c_str()));
    112   }
    113 
    114   return s;
    115 }
    116 
    117 void URLRequestPostInterceptor::Reset() {
    118   base::AutoLock auto_lock(interceptor_lock_);
    119   hit_count_ = 0;
    120   requests_.clear();
    121   ClearExpectations();
    122 }
    123 
    124 class URLRequestPostInterceptor::Delegate : public net::URLRequestInterceptor {
    125  public:
    126   Delegate(const std::string& scheme, const std::string& hostname)
    127       : scheme_(scheme), hostname_(hostname) {}
    128 
    129   void Register() {
    130     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    131     net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
    132         scheme_, hostname_, scoped_ptr<net::URLRequestInterceptor>(this));
    133   }
    134 
    135   void Unregister() {
    136     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    137     for (InterceptorMap::iterator it = interceptors_.begin();
    138          it != interceptors_.end();
    139          ++it)
    140       delete (*it).second;
    141     net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(scheme_,
    142                                                                 hostname_);
    143   }
    144 
    145   void OnCreateInterceptor(URLRequestPostInterceptor* interceptor) {
    146     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    147     CHECK(interceptors_.find(interceptor->GetUrl()) == interceptors_.end());
    148 
    149     interceptors_.insert(std::make_pair(interceptor->GetUrl(), interceptor));
    150   }
    151 
    152  private:
    153   virtual ~Delegate() {}
    154 
    155   virtual net::URLRequestJob* MaybeInterceptRequest(
    156       net::URLRequest* request,
    157       net::NetworkDelegate* network_delegate) const OVERRIDE {
    158     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    159 
    160     // Only intercepts POST.
    161     if (!request->has_upload())
    162       return NULL;
    163 
    164     GURL url = request->url();
    165     if (url.has_query()) {
    166       GURL::Replacements replacements;
    167       replacements.ClearQuery();
    168       url = url.ReplaceComponents(replacements);
    169     }
    170 
    171     InterceptorMap::const_iterator it(interceptors_.find(url));
    172     if (it == interceptors_.end())
    173       return NULL;
    174 
    175     // There is an interceptor hooked up for this url. Read the request body,
    176     // check the existing expectations, and handle the matching case by
    177     // popping the expectation off the queue, counting the match, and
    178     // returning a mock object to serve the canned response.
    179     URLRequestPostInterceptor* interceptor(it->second);
    180 
    181     const net::UploadDataStream* stream = request->get_upload();
    182     const net::UploadBytesElementReader* reader =
    183         stream->element_readers()[0]->AsBytesReader();
    184     const int size = reader->length();
    185     scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(size));
    186     const std::string request_body(reader->bytes());
    187 
    188     {
    189       base::AutoLock auto_lock(interceptor->interceptor_lock_);
    190       interceptor->requests_.push_back(request_body);
    191       if (interceptor->expectations_.empty())
    192         return NULL;
    193       const URLRequestPostInterceptor::Expectation& expectation(
    194           interceptor->expectations_.front());
    195       if (expectation.first->Match(request_body)) {
    196         const std::string response(expectation.second);
    197         delete expectation.first;
    198         interceptor->expectations_.pop();
    199         ++interceptor->hit_count_;
    200 
    201         return new URLRequestMockJob(request, network_delegate, response);
    202       }
    203     }
    204 
    205     return NULL;
    206   }
    207 
    208   typedef std::map<GURL, URLRequestPostInterceptor*> InterceptorMap;
    209   InterceptorMap interceptors_;
    210 
    211   const std::string scheme_;
    212   const std::string hostname_;
    213 
    214   DISALLOW_COPY_AND_ASSIGN(Delegate);
    215 };
    216 
    217 URLRequestPostInterceptorFactory::URLRequestPostInterceptorFactory(
    218     const std::string& scheme,
    219     const std::string& hostname)
    220     : scheme_(scheme),
    221       hostname_(hostname),
    222       delegate_(new URLRequestPostInterceptor::Delegate(scheme, hostname)) {
    223   BrowserThread::PostTask(
    224       BrowserThread::IO,
    225       FROM_HERE,
    226       base::Bind(&URLRequestPostInterceptor::Delegate::Register,
    227                  base::Unretained(delegate_)));
    228 }
    229 
    230 URLRequestPostInterceptorFactory::~URLRequestPostInterceptorFactory() {
    231   BrowserThread::PostTask(
    232       BrowserThread::IO,
    233       FROM_HERE,
    234       base::Bind(&URLRequestPostInterceptor::Delegate::Unregister,
    235                  base::Unretained(delegate_)));
    236 }
    237 
    238 URLRequestPostInterceptor* URLRequestPostInterceptorFactory::CreateInterceptor(
    239     const base::FilePath& filepath) {
    240   const GURL base_url(
    241       base::StringPrintf("%s://%s", scheme_.c_str(), hostname_.c_str()));
    242   GURL absolute_url(base_url.Resolve(filepath.MaybeAsASCII()));
    243   URLRequestPostInterceptor* interceptor(
    244       new URLRequestPostInterceptor(absolute_url));
    245   bool res = BrowserThread::PostTask(
    246       BrowserThread::IO,
    247       FROM_HERE,
    248       base::Bind(&URLRequestPostInterceptor::Delegate::OnCreateInterceptor,
    249                  base::Unretained(delegate_),
    250                  base::Unretained(interceptor)));
    251   if (!res) {
    252     delete interceptor;
    253     return NULL;
    254   }
    255 
    256   return interceptor;
    257 }
    258 
    259 }  // namespace component_updater
    260