Home | History | Annotate | Download | only in cloud
      1 // Copyright (c) 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/policy/cloud/test_request_interceptor.h"
      6 
      7 #include <limits>
      8 #include <queue>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "content/public/browser/browser_thread.h"
     14 #include "content/public/test/test_utils.h"
     15 #include "content/test/net/url_request_mock_http_job.h"
     16 #include "net/base/net_errors.h"
     17 #include "net/base/upload_bytes_element_reader.h"
     18 #include "net/base/upload_data_stream.h"
     19 #include "net/base/upload_element_reader.h"
     20 #include "net/url_request/url_request_error_job.h"
     21 #include "net/url_request/url_request_filter.h"
     22 #include "net/url_request/url_request_job_factory.h"
     23 #include "net/url_request/url_request_test_job.h"
     24 #include "url/gurl.h"
     25 
     26 namespace em = enterprise_management;
     27 
     28 namespace policy {
     29 
     30 namespace {
     31 
     32 // Helper to execute a |task| on IO, and return only after it has completed.
     33 void PostToIOAndWait(const base::Closure& task) {
     34   content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, task);
     35   content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
     36 }
     37 
     38 // Helper callback for jobs that should fail with a network |error|.
     39 net::URLRequestJob* ErrorJobCallback(int error,
     40                                      net::URLRequest* request,
     41                                      net::NetworkDelegate* network_delegate) {
     42   return new net::URLRequestErrorJob(request, network_delegate, error);
     43 }
     44 
     45 // Helper callback for jobs that should fail with a 400 HTTP error.
     46 net::URLRequestJob* BadRequestJobCallback(
     47     net::URLRequest* request,
     48     net::NetworkDelegate* network_delegate) {
     49   static const char kBadHeaders[] =
     50       "HTTP/1.1 400 Bad request\0"
     51       "Content-type: application/protobuf\0"
     52       "\0";
     53   std::string headers(kBadHeaders, arraysize(kBadHeaders));
     54   return new net::URLRequestTestJob(
     55       request, network_delegate, headers, std::string(), true);
     56 }
     57 
     58 net::URLRequestJob* FileJobCallback(const base::FilePath& file_path,
     59                                     net::URLRequest* request,
     60                                     net::NetworkDelegate* network_delegate) {
     61   return new content::URLRequestMockHTTPJob(
     62       request,
     63       network_delegate,
     64       file_path);
     65 }
     66 
     67 // Parses the upload data in |request| into |request_msg|, and validates the
     68 // request. The query string in the URL must contain the |expected_type| for
     69 // the "request" parameter. Returns true if all checks succeeded, and the
     70 // request data has been parsed into |request_msg|.
     71 bool ValidRequest(net::URLRequest* request,
     72                   const std::string& expected_type,
     73                   em::DeviceManagementRequest* request_msg) {
     74   if (request->method() != "POST")
     75     return false;
     76   std::string spec = request->url().spec();
     77   if (spec.find("request=" + expected_type) == std::string::npos)
     78     return false;
     79 
     80   // This assumes that the payload data was set from a single string. In that
     81   // case the UploadDataStream has a single UploadBytesElementReader with the
     82   // data in memory.
     83   const net::UploadDataStream* stream = request->get_upload();
     84   if (!stream)
     85     return false;
     86   const ScopedVector<net::UploadElementReader>& readers =
     87       stream->element_readers();
     88   if (readers.size() != 1u)
     89     return false;
     90   const net::UploadBytesElementReader* reader = readers[0]->AsBytesReader();
     91   if (!reader)
     92     return false;
     93   std::string data(reader->bytes(), reader->length());
     94   if (!request_msg->ParseFromString(data))
     95     return false;
     96 
     97   return true;
     98 }
     99 
    100 // Helper callback for register jobs that should suceed. Validates the request
    101 // parameters and returns an appropriate response job. If |expect_reregister|
    102 // is true then the reregister flag must be set in the DeviceRegisterRequest
    103 // protobuf.
    104 net::URLRequestJob* RegisterJobCallback(
    105     em::DeviceRegisterRequest::Type expected_type,
    106     bool expect_reregister,
    107     net::URLRequest* request,
    108     net::NetworkDelegate* network_delegate) {
    109   em::DeviceManagementRequest request_msg;
    110   if (!ValidRequest(request, "register", &request_msg))
    111     return BadRequestJobCallback(request, network_delegate);
    112 
    113   if (!request_msg.has_register_request() ||
    114       request_msg.has_unregister_request() ||
    115       request_msg.has_policy_request() ||
    116       request_msg.has_device_status_report_request() ||
    117       request_msg.has_session_status_report_request() ||
    118       request_msg.has_auto_enrollment_request()) {
    119     return BadRequestJobCallback(request, network_delegate);
    120   }
    121 
    122   const em::DeviceRegisterRequest& register_request =
    123       request_msg.register_request();
    124   if (expect_reregister &&
    125       (!register_request.has_reregister() || !register_request.reregister())) {
    126     return BadRequestJobCallback(request, network_delegate);
    127   } else if (!expect_reregister &&
    128              register_request.has_reregister() &&
    129              register_request.reregister()) {
    130     return BadRequestJobCallback(request, network_delegate);
    131   }
    132 
    133   if (!register_request.has_type() || register_request.type() != expected_type)
    134     return BadRequestJobCallback(request, network_delegate);
    135 
    136   em::DeviceManagementResponse response;
    137   em::DeviceRegisterResponse* register_response =
    138       response.mutable_register_response();
    139   register_response->set_device_management_token("s3cr3t70k3n");
    140   std::string data;
    141   response.SerializeToString(&data);
    142 
    143   static const char kGoodHeaders[] =
    144       "HTTP/1.1 200 OK\0"
    145       "Content-type: application/protobuf\0"
    146       "\0";
    147   std::string headers(kGoodHeaders, arraysize(kGoodHeaders));
    148   return new net::URLRequestTestJob(
    149       request, network_delegate, headers, data, true);
    150 }
    151 
    152 }  // namespace
    153 
    154 class TestRequestInterceptor::Delegate
    155     : public net::URLRequestJobFactory::ProtocolHandler {
    156  public:
    157   explicit Delegate(const std::string& hostname);
    158   virtual ~Delegate();
    159 
    160   // ProtocolHandler implementation:
    161   virtual net::URLRequestJob* MaybeCreateJob(
    162       net::URLRequest* request,
    163       net::NetworkDelegate* network_delegate) const OVERRIDE;
    164 
    165   void GetPendingSize(size_t* pending_size) const;
    166   void PushJobCallback(const JobCallback& callback);
    167 
    168  private:
    169   const std::string hostname_;
    170 
    171   // The queue of pending callbacks. 'mutable' because MaybeCreateJob() is a
    172   // const method; it can't reenter though, because it runs exclusively on
    173   // the IO thread.
    174   mutable std::queue<JobCallback> pending_job_callbacks_;
    175 };
    176 
    177 TestRequestInterceptor::Delegate::Delegate(const std::string& hostname)
    178     : hostname_(hostname) {}
    179 
    180 TestRequestInterceptor::Delegate::~Delegate() {}
    181 
    182 net::URLRequestJob* TestRequestInterceptor::Delegate::MaybeCreateJob(
    183     net::URLRequest* request,
    184     net::NetworkDelegate* network_delegate) const {
    185   CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    186 
    187   if (request->url().host() != hostname_) {
    188     // Reject requests to other servers.
    189     return ErrorJobCallback(
    190         net::ERR_CONNECTION_REFUSED, request, network_delegate);
    191   }
    192 
    193   if (pending_job_callbacks_.empty()) {
    194     // Reject dmserver requests by default.
    195     return BadRequestJobCallback(request, network_delegate);
    196   }
    197 
    198   JobCallback callback = pending_job_callbacks_.front();
    199   pending_job_callbacks_.pop();
    200   return callback.Run(request, network_delegate);
    201 }
    202 
    203 void TestRequestInterceptor::Delegate::GetPendingSize(
    204     size_t* pending_size) const {
    205   CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    206   *pending_size = pending_job_callbacks_.size();
    207 }
    208 
    209 void TestRequestInterceptor::Delegate::PushJobCallback(
    210     const JobCallback& callback) {
    211   CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    212   pending_job_callbacks_.push(callback);
    213 }
    214 
    215 TestRequestInterceptor::TestRequestInterceptor(const std::string& hostname)
    216     : hostname_(hostname) {
    217   delegate_ = new Delegate(hostname_);
    218   scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> handler(delegate_);
    219   PostToIOAndWait(
    220       base::Bind(&net::URLRequestFilter::AddHostnameProtocolHandler,
    221                  base::Unretained(net::URLRequestFilter::GetInstance()),
    222                  "http", hostname_, base::Passed(&handler)));
    223 }
    224 
    225 TestRequestInterceptor::~TestRequestInterceptor() {
    226   // RemoveHostnameHandler() destroys the |delegate_|, which is owned by
    227   // the URLRequestFilter.
    228   delegate_ = NULL;
    229   PostToIOAndWait(
    230       base::Bind(&net::URLRequestFilter::RemoveHostnameHandler,
    231                  base::Unretained(net::URLRequestFilter::GetInstance()),
    232                  "http", hostname_));
    233 }
    234 
    235 size_t TestRequestInterceptor::GetPendingSize() const {
    236   size_t pending_size = std::numeric_limits<size_t>::max();
    237   PostToIOAndWait(base::Bind(&Delegate::GetPendingSize,
    238                              base::Unretained(delegate_),
    239                              &pending_size));
    240   return pending_size;
    241 }
    242 
    243 void TestRequestInterceptor::PushJobCallback(const JobCallback& callback) {
    244   PostToIOAndWait(base::Bind(&Delegate::PushJobCallback,
    245                              base::Unretained(delegate_),
    246                              callback));
    247 }
    248 
    249 // static
    250 TestRequestInterceptor::JobCallback TestRequestInterceptor::ErrorJob(
    251     int error) {
    252   return base::Bind(&ErrorJobCallback, error);
    253 }
    254 
    255 // static
    256 TestRequestInterceptor::JobCallback TestRequestInterceptor::BadRequestJob() {
    257   return base::Bind(&BadRequestJobCallback);
    258 }
    259 
    260 // static
    261 TestRequestInterceptor::JobCallback TestRequestInterceptor::RegisterJob(
    262     em::DeviceRegisterRequest::Type expected_type,
    263     bool expect_reregister) {
    264   return base::Bind(&RegisterJobCallback, expected_type, expect_reregister);
    265 }
    266 
    267 // static
    268 TestRequestInterceptor::JobCallback TestRequestInterceptor::FileJob(
    269     const base::FilePath& file_path) {
    270   return base::Bind(&FileJobCallback, file_path);
    271 }
    272 
    273 }  // namespace policy
    274