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 |