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 "content/public/browser/browser_thread.h" 14 #include "content/public/test/test_utils.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_job_factory.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 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 net::URLRequestJob* FileJobCallback(const base::FilePath& file_path, 59 net::URLRequest* request, 60 net::NetworkDelegate* network_delegate) { 61 return new content::URLRequestMockHTTPJob( 62 request, 63 network_delegate, 64 file_path); 65 } 66 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; 79 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->element_readers(); 88 if (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; 96 97 return true; 98 } 99 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); 112 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); 120 } 121 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); 131 } 132 133 if (!register_request.has_type() || register_request.type() != expected_type) 134 return BadRequestJobCallback(request, network_delegate); 135 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); 142 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); 150 } 151 152 } // namespace 153 154 class TestRequestInterceptor::Delegate 155 : public net::URLRequestJobFactory::ProtocolHandler { 156 public: 157 explicit Delegate(const std::string& hostname); 158 virtual ~Delegate(); 159 160 // ProtocolHandler implementation: 161 virtual net::URLRequestJob* MaybeCreateJob( 162 net::URLRequest* request, 163 net::NetworkDelegate* network_delegate) const OVERRIDE; 164 165 void GetPendingSize(size_t* pending_size) const; 166 void PushJobCallback(const JobCallback& callback); 167 168 private: 169 const std::string hostname_; 170 171 // The queue of pending callbacks. 'mutable' because MaybeCreateJob() is a 172 // const method; it can't reenter though, because it runs exclusively on 173 // the IO thread. 174 mutable std::queue<JobCallback> pending_job_callbacks_; 175 }; 176 177 TestRequestInterceptor::Delegate::Delegate(const std::string& hostname) 178 : hostname_(hostname) {} 179 180 TestRequestInterceptor::Delegate::~Delegate() {} 181 182 net::URLRequestJob* TestRequestInterceptor::Delegate::MaybeCreateJob( 183 net::URLRequest* request, 184 net::NetworkDelegate* network_delegate) const { 185 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 186 187 if (request->url().host() != hostname_) { 188 // Reject requests to other servers. 189 return ErrorJobCallback( 190 net::ERR_CONNECTION_REFUSED, request, network_delegate); 191 } 192 193 if (pending_job_callbacks_.empty()) { 194 // Reject dmserver requests by default. 195 return BadRequestJobCallback(request, network_delegate); 196 } 197 198 JobCallback callback = pending_job_callbacks_.front(); 199 pending_job_callbacks_.pop(); 200 return callback.Run(request, network_delegate); 201 } 202 203 void TestRequestInterceptor::Delegate::GetPendingSize( 204 size_t* pending_size) const { 205 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 206 *pending_size = pending_job_callbacks_.size(); 207 } 208 209 void TestRequestInterceptor::Delegate::PushJobCallback( 210 const JobCallback& callback) { 211 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 212 pending_job_callbacks_.push(callback); 213 } 214 215 TestRequestInterceptor::TestRequestInterceptor(const std::string& hostname) 216 : hostname_(hostname) { 217 delegate_ = new Delegate(hostname_); 218 scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> handler(delegate_); 219 PostToIOAndWait( 220 base::Bind(&net::URLRequestFilter::AddHostnameProtocolHandler, 221 base::Unretained(net::URLRequestFilter::GetInstance()), 222 "http", hostname_, base::Passed(&handler))); 223 } 224 225 TestRequestInterceptor::~TestRequestInterceptor() { 226 // RemoveHostnameHandler() destroys the |delegate_|, which is owned by 227 // the URLRequestFilter. 228 delegate_ = NULL; 229 PostToIOAndWait( 230 base::Bind(&net::URLRequestFilter::RemoveHostnameHandler, 231 base::Unretained(net::URLRequestFilter::GetInstance()), 232 "http", hostname_)); 233 } 234 235 size_t TestRequestInterceptor::GetPendingSize() const { 236 size_t pending_size = std::numeric_limits<size_t>::max(); 237 PostToIOAndWait(base::Bind(&Delegate::GetPendingSize, 238 base::Unretained(delegate_), 239 &pending_size)); 240 return pending_size; 241 } 242 243 void TestRequestInterceptor::PushJobCallback(const JobCallback& callback) { 244 PostToIOAndWait(base::Bind(&Delegate::PushJobCallback, 245 base::Unretained(delegate_), 246 callback)); 247 } 248 249 // static 250 TestRequestInterceptor::JobCallback TestRequestInterceptor::ErrorJob( 251 int error) { 252 return base::Bind(&ErrorJobCallback, error); 253 } 254 255 // static 256 TestRequestInterceptor::JobCallback TestRequestInterceptor::BadRequestJob() { 257 return base::Bind(&BadRequestJobCallback); 258 } 259 260 // static 261 TestRequestInterceptor::JobCallback TestRequestInterceptor::RegisterJob( 262 em::DeviceRegisterRequest::Type expected_type, 263 bool expect_reregister) { 264 return base::Bind(&RegisterJobCallback, expected_type, expect_reregister); 265 } 266 267 // static 268 TestRequestInterceptor::JobCallback TestRequestInterceptor::FileJob( 269 const base::FilePath& file_path) { 270 return base::Bind(&FileJobCallback, file_path); 271 } 272 273 } // namespace policy 274