| 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 |