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 "chrome/browser/policy/proto/device_management_backend.pb.h" | |
14 #include "content/public/browser/browser_thread.h" | |
15 #include "content/public/test/test_utils.h" | |
16 #include "googleurl/src/gurl.h" | |
17 #include "net/base/net_errors.h" | |
18 #include "net/base/upload_bytes_element_reader.h" | |
19 #include "net/base/upload_data_stream.h" | |
20 #include "net/base/upload_element_reader.h" | |
21 #include "net/url_request/url_request_error_job.h" | |
22 #include "net/url_request/url_request_filter.h" | |
23 #include "net/url_request/url_request_job_factory.h" | |
24 #include "net/url_request/url_request_test_job.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 // Parses the upload data in |request| into |request_msg|, and validates the | |
59 // request. The query string in the URL must contain the |expected_type| for | |
60 // the "request" parameter. Returns true if all checks succeeded, and the | |
61 // request data has been parsed into |request_msg|. | |
62 bool ValidRequest(net::URLRequest* request, | |
63 const std::string& expected_type, | |
64 em::DeviceManagementRequest* request_msg) { | |
65 if (request->method() != "POST") | |
66 return false; | |
67 std::string spec = request->url().spec(); | |
68 if (spec.find("request=" + expected_type) == std::string::npos) | |
69 return false; | |
70 | |
71 // This assumes that the payload data was set from a single string. In that | |
72 // case the UploadDataStream has a single UploadBytesElementReader with the | |
73 // data in memory. | |
74 const net::UploadDataStream* stream = request->get_upload(); | |
75 if (!stream) | |
76 return false; | |
77 const ScopedVector<net::UploadElementReader>& readers = | |
78 stream->element_readers(); | |
79 if (readers.size() != 1u) | |
80 return false; | |
81 const net::UploadBytesElementReader* reader = readers[0]->AsBytesReader(); | |
82 if (!reader) | |
83 return false; | |
84 std::string data(reader->bytes(), reader->length()); | |
85 if (!request_msg->ParseFromString(data)) | |
86 return false; | |
87 | |
88 return true; | |
89 } | |
90 | |
91 // Helper callback for register jobs that should suceed. Validates the request | |
92 // parameters and returns an appropriate response job. If |expect_reregister| | |
93 // is true then the reregister flag must be set in the DeviceRegisterRequest | |
94 // protobuf. | |
95 net::URLRequestJob* RegisterJobCallback( | |
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 em::DeviceRegisterRequest::Type expected_type = | |
124 #if defined(OS_CHROMEOS) | |
125 em::DeviceRegisterRequest::USER; | |
126 #else | |
127 em::DeviceRegisterRequest::BROWSER; | |
128 #endif | |
Mattias Nissler (ping if slow)
2013/02/15 08:07:36
Should we pass this in? Otherwise this won't work
Joao da Silva
2013/02/15 12:30:19
Good idea, done.
| |
129 if (!register_request.has_type() || register_request.type() != expected_type) | |
130 return BadRequestJobCallback(request, network_delegate); | |
131 | |
132 em::DeviceManagementResponse response; | |
133 em::DeviceRegisterResponse* register_response = | |
134 response.mutable_register_response(); | |
135 register_response->set_device_management_token("s3cr3t70k3n"); | |
136 std::string data; | |
137 response.SerializeToString(&data); | |
138 | |
139 static const char kGoodHeaders[] = | |
140 "HTTP/1.1 200 OK\0" | |
141 "Content-type: application/protobuf\0" | |
142 "\0"; | |
143 std::string headers(kGoodHeaders, arraysize(kGoodHeaders)); | |
144 return new net::URLRequestTestJob( | |
145 request, network_delegate, headers, data, true); | |
146 } | |
147 | |
148 } // namespace | |
149 | |
150 class TestRequestInterceptor::Delegate | |
151 : public net::URLRequestJobFactory::ProtocolHandler { | |
152 public: | |
153 explicit Delegate(const std::string& hostname); | |
154 virtual ~Delegate(); | |
155 | |
156 // ProtocolHandler implementation: | |
157 virtual net::URLRequestJob* MaybeCreateJob( | |
158 net::URLRequest* request, | |
159 net::NetworkDelegate* network_delegate) const OVERRIDE; | |
160 | |
161 void GetPendingSize(size_t* pending_size) const; | |
162 void PushJobCallback(const JobCallback& callback); | |
163 | |
164 private: | |
165 const std::string hostname_; | |
166 | |
167 // The queue of pending callbacks. 'mutable' because MaybeCreateJob() is a | |
168 // const method; it can't reenter though, because it runs exclusively on | |
169 // the IO thread. | |
170 mutable std::queue<JobCallback> pending_job_callbacks_; | |
171 }; | |
172 | |
173 TestRequestInterceptor::Delegate::Delegate(const std::string& hostname) | |
174 : hostname_(hostname) {} | |
175 | |
176 TestRequestInterceptor::Delegate::~Delegate() {} | |
177 | |
178 net::URLRequestJob* TestRequestInterceptor::Delegate::MaybeCreateJob( | |
179 net::URLRequest* request, | |
180 net::NetworkDelegate* network_delegate) const { | |
181 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
182 | |
183 if (request->url().host() != hostname_) { | |
184 // Reject requests to other servers. | |
185 return ErrorJobCallback( | |
186 net::ERR_CONNECTION_REFUSED, request, network_delegate); | |
187 } | |
188 | |
189 if (pending_job_callbacks_.empty()) { | |
190 // Reject dmserver requests by default. | |
191 return BadRequestJobCallback(request, network_delegate); | |
192 } | |
193 | |
194 JobCallback callback = pending_job_callbacks_.front(); | |
195 pending_job_callbacks_.pop(); | |
196 return callback.Run(request, network_delegate); | |
197 } | |
198 | |
199 void TestRequestInterceptor::Delegate::GetPendingSize( | |
200 size_t* pending_size) const { | |
201 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
202 *pending_size = pending_job_callbacks_.size(); | |
203 } | |
204 | |
205 void TestRequestInterceptor::Delegate::PushJobCallback( | |
206 const JobCallback& callback) { | |
207 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
208 pending_job_callbacks_.push(callback); | |
209 } | |
210 | |
211 TestRequestInterceptor::TestRequestInterceptor(const std::string& hostname) | |
212 : hostname_(hostname) { | |
213 delegate_ = new Delegate(hostname_); | |
214 scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> handler(delegate_); | |
215 PostToIOAndWait( | |
216 base::Bind(&net::URLRequestFilter::AddHostnameProtocolHandler, | |
217 base::Unretained(net::URLRequestFilter::GetInstance()), | |
218 "http", hostname_, base::Passed(&handler))); | |
219 } | |
220 | |
221 TestRequestInterceptor::~TestRequestInterceptor() { | |
222 // RemoveHostnameHandler() destroys the |delegate_|, which is owned by | |
223 // the URLRequestFilter. | |
224 delegate_ = NULL; | |
225 PostToIOAndWait( | |
226 base::Bind(&net::URLRequestFilter::RemoveHostnameHandler, | |
227 base::Unretained(net::URLRequestFilter::GetInstance()), | |
228 "http", hostname_)); | |
229 } | |
230 | |
231 size_t TestRequestInterceptor::GetPendingSize() const { | |
232 size_t pending_size = std::numeric_limits<size_t>::max(); | |
233 PostToIOAndWait(base::Bind(&Delegate::GetPendingSize, | |
234 base::Unretained(delegate_), | |
235 &pending_size)); | |
236 return pending_size; | |
237 } | |
238 | |
239 void TestRequestInterceptor::PushJobCallback(const JobCallback& callback) { | |
240 PostToIOAndWait(base::Bind(&Delegate::PushJobCallback, | |
241 base::Unretained(delegate_), | |
242 callback)); | |
243 } | |
244 | |
245 // static | |
246 TestRequestInterceptor::JobCallback TestRequestInterceptor::ErrorJob( | |
247 int error) { | |
248 return base::Bind(&ErrorJobCallback, error); | |
249 } | |
250 | |
251 // static | |
252 TestRequestInterceptor::JobCallback TestRequestInterceptor::BadRequestJob() { | |
253 return base::Bind(&BadRequestJobCallback); | |
254 } | |
255 | |
256 // static | |
257 TestRequestInterceptor::JobCallback TestRequestInterceptor::RegisterJob( | |
258 bool expect_reregister) { | |
259 return base::Bind(&RegisterJobCallback, expect_reregister); | |
260 } | |
261 | |
262 } // namespace policy | |
OLD | NEW |