Chromium Code Reviews| 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 |