Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / policy / cloud / test_request_interceptor.cc
blob78b5511f0702d7a7a14cdb8fc9f07ed1dc0d4014
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.
5 #include "chrome/browser/policy/cloud/test_request_interceptor.h"
7 #include <limits>
8 #include <queue>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/run_loop.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "net/base/net_errors.h"
19 #include "net/base/upload_bytes_element_reader.h"
20 #include "net/base/upload_data_stream.h"
21 #include "net/base/upload_element_reader.h"
22 #include "net/test/url_request/url_request_mock_http_job.h"
23 #include "net/url_request/url_request_error_job.h"
24 #include "net/url_request/url_request_filter.h"
25 #include "net/url_request/url_request_interceptor.h"
26 #include "net/url_request/url_request_test_job.h"
27 #include "url/gurl.h"
29 namespace em = enterprise_management;
31 namespace policy {
33 namespace {
35 // Helper callback for jobs that should fail with a network |error|.
36 net::URLRequestJob* ErrorJobCallback(int error,
37 net::URLRequest* request,
38 net::NetworkDelegate* network_delegate) {
39 return new net::URLRequestErrorJob(request, network_delegate, error);
42 // Helper callback for jobs that should fail with a 400 HTTP error.
43 net::URLRequestJob* BadRequestJobCallback(
44 net::URLRequest* request,
45 net::NetworkDelegate* network_delegate) {
46 static const char kBadHeaders[] =
47 "HTTP/1.1 400 Bad request\0"
48 "Content-type: application/protobuf\0"
49 "\0";
50 std::string headers(kBadHeaders, arraysize(kBadHeaders));
51 return new net::URLRequestTestJob(
52 request, network_delegate, headers, std::string(), true);
55 net::URLRequestJob* FileJobCallback(const base::FilePath& file_path,
56 net::URLRequest* request,
57 net::NetworkDelegate* network_delegate) {
58 return new net::URLRequestMockHTTPJob(
59 request,
60 network_delegate,
61 file_path,
62 content::BrowserThread::GetBlockingPool()
63 ->GetTaskRunnerWithShutdownBehavior(
64 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
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;
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->GetElementReaders();
88 if (!readers || 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;
97 return true;
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);
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);
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);
133 if (!register_request.has_type() || register_request.type() != expected_type)
134 return BadRequestJobCallback(request, network_delegate);
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);
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);
152 void RegisterHttpInterceptor(
153 const std::string& hostname,
154 scoped_ptr<net::URLRequestInterceptor> interceptor) {
155 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
156 "http", hostname, interceptor.Pass());
159 void UnregisterHttpInterceptor(const std::string& hostname) {
160 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler("http", hostname);
163 } // namespace
165 class TestRequestInterceptor::Delegate : public net::URLRequestInterceptor {
166 public:
167 Delegate(const std::string& hostname,
168 scoped_refptr<base::SequencedTaskRunner> io_task_runner);
169 ~Delegate() override;
171 // net::URLRequestInterceptor implementation:
172 net::URLRequestJob* MaybeInterceptRequest(
173 net::URLRequest* request,
174 net::NetworkDelegate* network_delegate) const override;
176 void GetPendingSize(size_t* pending_size) const;
177 void AddRequestServicedCallback(const base::Closure& callback);
178 void PushJobCallback(const JobCallback& callback);
180 private:
181 static void InvokeRequestServicedCallbacks(
182 scoped_ptr<std::vector<base::Closure>> callbacks);
184 const std::string hostname_;
185 scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
187 // The queue of pending callbacks. 'mutable' because MaybeCreateJob() is a
188 // const method; it can't reenter though, because it runs exclusively on
189 // the IO thread.
190 mutable std::queue<JobCallback> pending_job_callbacks_;
192 // Queue of pending request serviced callbacks. Mutable for the same reason
193 // as |pending_job_callbacks_|.
194 mutable std::vector<base::Closure> request_serviced_callbacks_;
197 TestRequestInterceptor::Delegate::Delegate(
198 const std::string& hostname,
199 scoped_refptr<base::SequencedTaskRunner> io_task_runner)
200 : hostname_(hostname), io_task_runner_(io_task_runner) {}
202 TestRequestInterceptor::Delegate::~Delegate() {}
204 net::URLRequestJob* TestRequestInterceptor::Delegate::MaybeInterceptRequest(
205 net::URLRequest* request,
206 net::NetworkDelegate* network_delegate) const {
207 CHECK(io_task_runner_->RunsTasksOnCurrentThread());
209 if (request->url().host() != hostname_) {
210 // Reject requests to other servers.
211 return ErrorJobCallback(
212 net::ERR_CONNECTION_REFUSED, request, network_delegate);
215 if (pending_job_callbacks_.empty()) {
216 // Reject dmserver requests by default.
217 return BadRequestJobCallback(request, network_delegate);
220 // Invoke any callbacks that are waiting for the next request to be serviced
221 // after this job is serviced.
222 if (!request_serviced_callbacks_.empty()) {
223 scoped_ptr<std::vector<base::Closure>> callbacks(
224 new std::vector<base::Closure>);
225 callbacks->swap(request_serviced_callbacks_);
226 io_task_runner_->PostTask(
227 FROM_HERE, base::Bind(&Delegate::InvokeRequestServicedCallbacks,
228 base::Passed(&callbacks)));
231 JobCallback callback = pending_job_callbacks_.front();
232 pending_job_callbacks_.pop();
233 return callback.Run(request, network_delegate);
236 void TestRequestInterceptor::Delegate::GetPendingSize(
237 size_t* pending_size) const {
238 CHECK(io_task_runner_->RunsTasksOnCurrentThread());
239 *pending_size = pending_job_callbacks_.size();
242 void TestRequestInterceptor::Delegate::AddRequestServicedCallback(
243 const base::Closure& callback) {
244 CHECK(io_task_runner_->RunsTasksOnCurrentThread());
245 request_serviced_callbacks_.push_back(callback);
248 void TestRequestInterceptor::Delegate::PushJobCallback(
249 const JobCallback& callback) {
250 CHECK(io_task_runner_->RunsTasksOnCurrentThread());
251 pending_job_callbacks_.push(callback);
254 // static
255 void TestRequestInterceptor::Delegate::InvokeRequestServicedCallbacks(
256 scoped_ptr<std::vector<base::Closure>> callbacks) {
257 for (const auto& p : *callbacks)
258 p.Run();
261 TestRequestInterceptor::TestRequestInterceptor(const std::string& hostname,
262 scoped_refptr<base::SequencedTaskRunner> io_task_runner)
263 : hostname_(hostname),
264 io_task_runner_(io_task_runner) {
265 delegate_ = new Delegate(hostname_, io_task_runner_);
266 scoped_ptr<net::URLRequestInterceptor> interceptor(delegate_);
267 PostToIOAndWait(
268 base::Bind(&RegisterHttpInterceptor, hostname_,
269 base::Passed(&interceptor)));
272 TestRequestInterceptor::~TestRequestInterceptor() {
273 // RemoveHostnameHandler() destroys the |delegate_|, which is owned by
274 // the URLRequestFilter.
275 delegate_ = NULL;
276 PostToIOAndWait(base::Bind(&UnregisterHttpInterceptor, hostname_));
279 size_t TestRequestInterceptor::GetPendingSize() {
280 size_t pending_size = std::numeric_limits<size_t>::max();
281 PostToIOAndWait(base::Bind(&Delegate::GetPendingSize,
282 base::Unretained(delegate_),
283 &pending_size));
284 return pending_size;
287 void TestRequestInterceptor::AddRequestServicedCallback(
288 const base::Closure& callback) {
289 base::Closure post_callback =
290 base::Bind(base::IgnoreResult(&base::TaskRunner::PostTask),
291 base::ThreadTaskRunnerHandle::Get(),
292 FROM_HERE,
293 callback);
294 PostToIOAndWait(base::Bind(&Delegate::AddRequestServicedCallback,
295 base::Unretained(delegate_),
296 post_callback));
299 void TestRequestInterceptor::PushJobCallback(const JobCallback& callback) {
300 PostToIOAndWait(base::Bind(&Delegate::PushJobCallback,
301 base::Unretained(delegate_),
302 callback));
305 // static
306 TestRequestInterceptor::JobCallback TestRequestInterceptor::ErrorJob(
307 int error) {
308 return base::Bind(&ErrorJobCallback, error);
311 // static
312 TestRequestInterceptor::JobCallback TestRequestInterceptor::BadRequestJob() {
313 return base::Bind(&BadRequestJobCallback);
316 // static
317 TestRequestInterceptor::JobCallback TestRequestInterceptor::RegisterJob(
318 em::DeviceRegisterRequest::Type expected_type,
319 bool expect_reregister) {
320 return base::Bind(&RegisterJobCallback, expected_type, expect_reregister);
323 // static
324 TestRequestInterceptor::JobCallback TestRequestInterceptor::FileJob(
325 const base::FilePath& file_path) {
326 return base::Bind(&FileJobCallback, file_path);
329 void TestRequestInterceptor::PostToIOAndWait(const base::Closure& task) {
330 io_task_runner_->PostTask(FROM_HERE, task);
331 base::RunLoop run_loop;
332 io_task_runner_->PostTask(
333 FROM_HERE,
334 base::Bind(
335 base::IgnoreResult(&base::TaskRunner::PostTask),
336 base::ThreadTaskRunnerHandle::Get(),
337 FROM_HERE,
338 run_loop.QuitClosure()));
339 run_loop.Run();
342 } // namespace policy