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