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