Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(700)

Side by Side Diff: net/test/embedded_test_server/embedded_test_server.cc

Issue 1376593007: SSL in EmbeddedTestServer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Typo fix. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/test/embedded_test_server/embedded_test_server.h" 5 #include "net/test/embedded_test_server/embedded_test_server.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/files/file_path.h" 8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h" 9 #include "base/files/file_util.h"
10 #include "base/location.h" 10 #include "base/location.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h" 12 #include "base/message_loop/message_loop.h"
13 #include "base/path_service.h"
13 #include "base/process/process_metrics.h" 14 #include "base/process/process_metrics.h"
14 #include "base/run_loop.h" 15 #include "base/run_loop.h"
15 #include "base/stl_util.h" 16 #include "base/stl_util.h"
16 #include "base/strings/string_util.h" 17 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h" 18 #include "base/strings/stringprintf.h"
18 #include "base/thread_task_runner_handle.h" 19 #include "base/thread_task_runner_handle.h"
19 #include "base/threading/thread_restrictions.h" 20 #include "base/threading/thread_restrictions.h"
21 #include "crypto/rsa_private_key.h"
20 #include "net/base/ip_endpoint.h" 22 #include "net/base/ip_endpoint.h"
21 #include "net/base/net_errors.h" 23 #include "net/base/net_errors.h"
24 #include "net/base/test_data_directory.h"
25 #include "net/cert/pem_tokenizer.h"
26 #include "net/cert/test_root_certs.h"
27 #include "net/socket/ssl_server_socket.h"
22 #include "net/socket/stream_socket.h" 28 #include "net/socket/stream_socket.h"
23 #include "net/socket/tcp_server_socket.h" 29 #include "net/socket/tcp_server_socket.h"
30 #include "net/ssl/ssl_server_config.h"
31 #include "net/test/cert_test_util.h"
24 #include "net/test/embedded_test_server/embedded_test_server_connection_listener .h" 32 #include "net/test/embedded_test_server/embedded_test_server_connection_listener .h"
25 #include "net/test/embedded_test_server/http_connection.h" 33 #include "net/test/embedded_test_server/http_connection.h"
26 #include "net/test/embedded_test_server/http_request.h" 34 #include "net/test/embedded_test_server/http_request.h"
27 #include "net/test/embedded_test_server/http_response.h" 35 #include "net/test/embedded_test_server/http_response.h"
36 #include "net/test/embedded_test_server/request_handler_util.h"
28 37
29 namespace net { 38 namespace net {
30 namespace test_server { 39 namespace test_server {
31 40
32 namespace { 41 EmbeddedTestServer::EmbeddedTestServer() : EmbeddedTestServer(TYPE_HTTP) {}
33 42
34 class CustomHttpResponse : public HttpResponse { 43 EmbeddedTestServer::EmbeddedTestServer(Type type)
35 public: 44 : is_using_ssl_(type == TYPE_HTTPS),
36 CustomHttpResponse(const std::string& headers, const std::string& contents) 45 connection_listener_(nullptr),
37 : headers_(headers), contents_(contents) { 46 port_(0),
47 cert_(CERT_OK) {
48 DCHECK(thread_checker_.CalledOnValidThread());
49
50 if (is_using_ssl_) {
51 TestRootCerts* root_certs = TestRootCerts::GetInstance();
52 base::FilePath certs_dir(GetTestCertsDirectory());
53 root_certs->AddFromFile(certs_dir.AppendASCII("root_ca_cert.pem"));
38 } 54 }
39
40 std::string ToResponseString() const override {
41 return headers_ + "\r\n" + contents_;
42 }
43
44 private:
45 std::string headers_;
46 std::string contents_;
47
48 DISALLOW_COPY_AND_ASSIGN(CustomHttpResponse);
49 };
50
51 // Handles |request| by serving a file from under |server_root|.
52 scoped_ptr<HttpResponse> HandleFileRequest(
53 const base::FilePath& server_root,
54 const HttpRequest& request) {
55 // This is a test-only server. Ignore I/O thread restrictions.
56 base::ThreadRestrictions::ScopedAllowIO allow_io;
57
58 std::string relative_url(request.relative_url);
59 // A proxy request will have an absolute path. Simulate the proxy by stripping
60 // the scheme, host, and port.
61 GURL relative_gurl(relative_url);
62 if (relative_gurl.is_valid())
63 relative_url = relative_gurl.PathForRequest();
64
65 // Trim the first byte ('/').
66 std::string request_path = relative_url.substr(1);
67
68 // Remove the query string if present.
69 size_t query_pos = request_path.find('?');
70 if (query_pos != std::string::npos)
71 request_path = request_path.substr(0, query_pos);
72
73 base::FilePath file_path(server_root.AppendASCII(request_path));
74 std::string file_contents;
75 if (!base::ReadFileToString(file_path, &file_contents))
76 return scoped_ptr<HttpResponse>();
77
78 base::FilePath headers_path(
79 file_path.AddExtension(FILE_PATH_LITERAL("mock-http-headers")));
80
81 if (base::PathExists(headers_path)) {
82 std::string headers_contents;
83 if (!base::ReadFileToString(headers_path, &headers_contents))
84 return scoped_ptr<HttpResponse>();
85
86 scoped_ptr<CustomHttpResponse> http_response(
87 new CustomHttpResponse(headers_contents, file_contents));
88 return http_response.Pass();
89 }
90
91 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
92 http_response->set_code(HTTP_OK);
93 http_response->set_content(file_contents);
94 return http_response.Pass();
95 }
96
97 } // namespace
98
99 EmbeddedTestServer::EmbeddedTestServer()
100 : connection_listener_(nullptr), port_(0) {
101 DCHECK(thread_checker_.CalledOnValidThread());
102 } 55 }
103 56
104 EmbeddedTestServer::~EmbeddedTestServer() { 57 EmbeddedTestServer::~EmbeddedTestServer() {
105 DCHECK(thread_checker_.CalledOnValidThread()); 58 DCHECK(thread_checker_.CalledOnValidThread());
106 59
107 if (Started() && !ShutdownAndWaitUntilComplete()) { 60 if (Started() && !ShutdownAndWaitUntilComplete()) {
108 LOG(ERROR) << "EmbeddedTestServer failed to shut down."; 61 LOG(ERROR) << "EmbeddedTestServer failed to shut down.";
109 } 62 }
110 } 63 }
111 64
112 void EmbeddedTestServer::SetConnectionListener( 65 void EmbeddedTestServer::SetConnectionListener(
113 EmbeddedTestServerConnectionListener* listener) { 66 EmbeddedTestServerConnectionListener* listener) {
114 DCHECK(!Started()); 67 DCHECK(!Started());
115 connection_listener_ = listener; 68 connection_listener_ = listener;
116 } 69 }
117 70
118 bool EmbeddedTestServer::InitializeAndWaitUntilReady() { 71 bool EmbeddedTestServer::Start() {
119 bool success = InitializeAndListen(); 72 bool success = InitializeAndListen();
120 if (!success) 73 if (!success)
121 return false; 74 return false;
122 StartAcceptingConnections(); 75 StartAcceptingConnections();
123 return true; 76 return true;
124 } 77 }
125 78
79 bool EmbeddedTestServer::InitializeAndWaitUntilReady() {
80 return Start();
81 }
82
126 bool EmbeddedTestServer::InitializeAndListen() { 83 bool EmbeddedTestServer::InitializeAndListen() {
127 DCHECK(!Started()); 84 DCHECK(!Started());
128 85
129 listen_socket_.reset(new TCPServerSocket(nullptr, NetLog::Source())); 86 listen_socket_.reset(new TCPServerSocket(nullptr, NetLog::Source()));
130 87
131 int result = listen_socket_->ListenWithAddressAndPort("127.0.0.1", 0, 10); 88 int result = listen_socket_->ListenWithAddressAndPort("127.0.0.1", 0, 10);
132 if (result) { 89 if (result) {
133 LOG(ERROR) << "Listen failed: " << ErrorToString(result); 90 LOG(ERROR) << "Listen failed: " << ErrorToString(result);
134 listen_socket_.reset(); 91 listen_socket_.reset();
135 return false; 92 return false;
136 } 93 }
137 94
138 result = listen_socket_->GetLocalAddress(&local_endpoint_); 95 result = listen_socket_->GetLocalAddress(&local_endpoint_);
139 if (result != OK) { 96 if (result != OK) {
140 LOG(ERROR) << "GetLocalAddress failed: " << ErrorToString(result); 97 LOG(ERROR) << "GetLocalAddress failed: " << ErrorToString(result);
141 listen_socket_.reset(); 98 listen_socket_.reset();
142 return false; 99 return false;
143 } 100 }
144 101
145 base_url_ = GURL(std::string("http://") + local_endpoint_.ToString()); 102 if (is_using_ssl_) {
103 base_url_ = GURL("https://" + local_endpoint_.ToString());
104 if (cert_ == CERT_MISMATCHED_NAME || cert_ == CERT_COMMON_NAME_IS_DOMAIN) {
105 base_url_ = GURL(
106 base::StringPrintf("https://localhost:%d", local_endpoint_.port()));
107 }
108 } else {
109 base_url_ = GURL("http://" + local_endpoint_.ToString());
110 }
146 port_ = local_endpoint_.port(); 111 port_ = local_endpoint_.port();
147 112
148 listen_socket_->DetachFromThread(); 113 listen_socket_->DetachFromThread();
149 return true; 114 return true;
150 } 115 }
151 116
152 void EmbeddedTestServer::StartAcceptingConnections() { 117 void EmbeddedTestServer::StartAcceptingConnections() {
153 DCHECK(!io_thread_.get()); 118 DCHECK(!io_thread_.get());
154 base::Thread::Options thread_options; 119 base::Thread::Options thread_options;
155 thread_options.message_loop_type = base::MessageLoop::TYPE_IO; 120 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
(...skipping 20 matching lines...) Expand all
176 connections_.end()); 141 connections_.end());
177 connections_.clear(); 142 connections_.clear();
178 } 143 }
179 144
180 void EmbeddedTestServer::HandleRequest(HttpConnection* connection, 145 void EmbeddedTestServer::HandleRequest(HttpConnection* connection,
181 scoped_ptr<HttpRequest> request) { 146 scoped_ptr<HttpRequest> request) {
182 DCHECK(io_thread_->task_runner()->BelongsToCurrentThread()); 147 DCHECK(io_thread_->task_runner()->BelongsToCurrentThread());
183 148
184 scoped_ptr<HttpResponse> response; 149 scoped_ptr<HttpResponse> response;
185 150
186 for (size_t i = 0; i < request_handlers_.size(); ++i) { 151 for (const auto& handler : request_handlers_) {
187 response = request_handlers_[i].Run(*request); 152 response = handler.Run(*request);
188 if (response) 153 if (response)
189 break; 154 break;
190 } 155 }
191 156
192 if (!response) { 157 if (!response) {
158 for (const auto& handler : default_request_handlers_) {
159 response = handler.Run(*request);
160 if (response)
161 break;
162 }
163 }
164
165 if (!response) {
193 LOG(WARNING) << "Request not handled. Returning 404: " 166 LOG(WARNING) << "Request not handled. Returning 404: "
194 << request->relative_url; 167 << request->relative_url;
195 scoped_ptr<BasicHttpResponse> not_found_response(new BasicHttpResponse); 168 scoped_ptr<BasicHttpResponse> not_found_response(new BasicHttpResponse);
196 not_found_response->set_code(HTTP_NOT_FOUND); 169 not_found_response->set_code(HTTP_NOT_FOUND);
197 response = not_found_response.Pass(); 170 response = not_found_response.Pass();
198 } 171 }
199 172
200 connection->SendResponse(response.Pass(), 173 response->SendResponse(base::Bind(&HttpConnection::SendResponseBytes,
201 base::Bind(&EmbeddedTestServer::DidClose, 174 base::Unretained(connection)),
202 base::Unretained(this), connection)); 175 base::Bind(&EmbeddedTestServer::DidClose,
176 base::Unretained(this), connection));
203 } 177 }
204 178
205 GURL EmbeddedTestServer::GetURL(const std::string& relative_url) const { 179 GURL EmbeddedTestServer::GetURL(const std::string& relative_url) const {
206 DCHECK(Started()) << "You must start the server first."; 180 DCHECK(Started()) << "You must start the server first.";
207 DCHECK(base::StartsWith(relative_url, "/", base::CompareCase::SENSITIVE)) 181 DCHECK(base::StartsWith(relative_url, "/", base::CompareCase::SENSITIVE))
208 << relative_url; 182 << relative_url;
209 return base_url_.Resolve(relative_url); 183 return base_url_.Resolve(relative_url);
210 } 184 }
211 185
212 GURL EmbeddedTestServer::GetURL( 186 GURL EmbeddedTestServer::GetURL(
213 const std::string& hostname, 187 const std::string& hostname,
214 const std::string& relative_url) const { 188 const std::string& relative_url) const {
215 GURL local_url = GetURL(relative_url); 189 GURL local_url = GetURL(relative_url);
216 GURL::Replacements replace_host; 190 GURL::Replacements replace_host;
217 replace_host.SetHostStr(hostname); 191 replace_host.SetHostStr(hostname);
218 return local_url.ReplaceComponents(replace_host); 192 return local_url.ReplaceComponents(replace_host);
219 } 193 }
220 194
221 bool EmbeddedTestServer::GetAddressList(AddressList* address_list) const { 195 bool EmbeddedTestServer::GetAddressList(AddressList* address_list) const {
222 *address_list = AddressList(local_endpoint_); 196 *address_list = AddressList(local_endpoint_);
223 return true; 197 return true;
224 } 198 }
225 199
200 void EmbeddedTestServer::SetSSLConfig(ServerCertificate cert,
201 const SSLServerConfig& ssl_config) {
202 DCHECK(!Started());
203 cert_ = cert;
204 ssl_config_ = ssl_config;
205 }
206
207 void EmbeddedTestServer::SetSSLConfig(ServerCertificate cert) {
208 SetSSLConfig(cert, SSLServerConfig());
209 }
210
211 std::string EmbeddedTestServer::GetCertificateName() const {
212 DCHECK(is_using_ssl_);
213 switch (cert_) {
214 case CERT_OK:
215 case CERT_MISMATCHED_NAME:
216 return "ok_cert.pem";
217 case CERT_COMMON_NAME_IS_DOMAIN:
218 return "localhost_cert.pem";
219 case CERT_EXPIRED:
220 return "expired_cert.pem";
221 case CERT_CHAIN_WRONG_ROOT:
222 return "redundant-server-chain.pem";
223 case CERT_BAD_VALIDITY:
224 return "bad_validity.pem";
225 }
226
227 return "ok_cert.pem";
228 }
229
230 scoped_refptr<X509Certificate> EmbeddedTestServer::GetCertificate() const {
231 DCHECK(is_using_ssl_);
232 base::FilePath certs_dir(GetTestCertsDirectory());
233 return ImportCertFromFile(certs_dir, GetCertificateName());
234 }
235
226 void EmbeddedTestServer::ServeFilesFromDirectory( 236 void EmbeddedTestServer::ServeFilesFromDirectory(
227 const base::FilePath& directory) { 237 const base::FilePath& directory) {
228 RegisterRequestHandler(base::Bind(&HandleFileRequest, directory)); 238 RegisterRequestHandler(base::Bind(&HandleFileRequest, directory));
229 } 239 }
230 240
241 void EmbeddedTestServer::ServeFilesFromSourceDirectory(
242 const std::string& relative) {
243 base::FilePath test_data_dir;
244 CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
245 ServeFilesFromDirectory(test_data_dir.AppendASCII(relative));
246 }
247
248 void EmbeddedTestServer::ServeFilesFromSourceDirectory(
249 const base::FilePath& relative) {
250 base::FilePath test_data_dir;
251 CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
252 ServeFilesFromDirectory(test_data_dir.Append(relative));
253 }
254
255 void EmbeddedTestServer::AddDefaultHandlers(const base::FilePath& directory) {
256 // TODO(svaldez): Add additional default handlers.
257 ServeFilesFromSourceDirectory(directory);
258 }
259
231 void EmbeddedTestServer::RegisterRequestHandler( 260 void EmbeddedTestServer::RegisterRequestHandler(
232 const HandleRequestCallback& callback) { 261 const HandleRequestCallback& callback) {
262 // TODO(svaldez): Add check to prevent RegisterHandler from being called
263 // after the server has started. https://crbug.com/546060
233 request_handlers_.push_back(callback); 264 request_handlers_.push_back(callback);
234 } 265 }
235 266
267 void EmbeddedTestServer::RegisterDefaultHandler(
268 const HandleRequestCallback& callback) {
269 // TODO(svaldez): Add check to prevent RegisterHandler from being called
270 // after the server has started. https://crbug.com/546060
271 default_request_handlers_.push_back(callback);
272 }
273
274 scoped_ptr<StreamSocket> EmbeddedTestServer::DoSSLUpgrade(
275 scoped_ptr<StreamSocket> connection) {
276 DCHECK(io_thread_->task_runner()->BelongsToCurrentThread());
277
278 base::FilePath certs_dir(GetTestCertsDirectory());
279 std::string cert_name = GetCertificateName();
280
281 base::FilePath key_path = certs_dir.AppendASCII(cert_name);
282 std::string key_string;
283 CHECK(base::ReadFileToString(key_path, &key_string));
284 std::vector<std::string> headers;
285 headers.push_back("PRIVATE KEY");
286 PEMTokenizer pem_tokenizer(key_string, headers);
287 pem_tokenizer.GetNext();
288 std::vector<uint8_t> key_vector;
289 key_vector.assign(pem_tokenizer.data().begin(), pem_tokenizer.data().end());
290
291 scoped_ptr<crypto::RSAPrivateKey> server_key(
292 crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector));
293
294 return CreateSSLServerSocket(connection.Pass(), GetCertificate().get(),
295 server_key.get(), ssl_config_);
296 }
297
236 void EmbeddedTestServer::DoAcceptLoop() { 298 void EmbeddedTestServer::DoAcceptLoop() {
237 int rv = OK; 299 int rv = OK;
238 while (rv == OK) { 300 while (rv == OK) {
239 rv = listen_socket_->Accept( 301 rv = listen_socket_->Accept(
240 &accepted_socket_, base::Bind(&EmbeddedTestServer::OnAcceptCompleted, 302 &accepted_socket_, base::Bind(&EmbeddedTestServer::OnAcceptCompleted,
241 base::Unretained(this))); 303 base::Unretained(this)));
242 if (rv == ERR_IO_PENDING) 304 if (rv == ERR_IO_PENDING)
243 return; 305 return;
244 HandleAcceptResult(accepted_socket_.Pass()); 306 HandleAcceptResult(accepted_socket_.Pass());
245 } 307 }
246 } 308 }
247 309
248 void EmbeddedTestServer::OnAcceptCompleted(int rv) { 310 void EmbeddedTestServer::OnAcceptCompleted(int rv) {
249 DCHECK_NE(ERR_IO_PENDING, rv); 311 DCHECK_NE(ERR_IO_PENDING, rv);
250 HandleAcceptResult(accepted_socket_.Pass()); 312 HandleAcceptResult(accepted_socket_.Pass());
251 DoAcceptLoop(); 313 DoAcceptLoop();
252 } 314 }
253 315
316 void EmbeddedTestServer::OnHandshakeDone(HttpConnection* connection, int rv) {
317 if (connection->socket_->IsConnected())
318 ReadData(connection);
319 else
320 DidClose(connection);
321 }
322
254 void EmbeddedTestServer::HandleAcceptResult(scoped_ptr<StreamSocket> socket) { 323 void EmbeddedTestServer::HandleAcceptResult(scoped_ptr<StreamSocket> socket) {
255 DCHECK(io_thread_->task_runner()->BelongsToCurrentThread()); 324 DCHECK(io_thread_->task_runner()->BelongsToCurrentThread());
256 if (connection_listener_) 325 if (connection_listener_)
257 connection_listener_->AcceptedSocket(*socket); 326 connection_listener_->AcceptedSocket(*socket);
258 327
328 if (is_using_ssl_)
329 socket = DoSSLUpgrade(socket.Pass());
330
259 HttpConnection* http_connection = new HttpConnection( 331 HttpConnection* http_connection = new HttpConnection(
260 socket.Pass(), 332 socket.Pass(),
261 base::Bind(&EmbeddedTestServer::HandleRequest, base::Unretained(this))); 333 base::Bind(&EmbeddedTestServer::HandleRequest, base::Unretained(this)));
262 connections_[http_connection->socket_.get()] = http_connection; 334 connections_[http_connection->socket_.get()] = http_connection;
263 335
264 ReadData(http_connection); 336 if (is_using_ssl_) {
337 SSLServerSocket* ssl_socket =
338 static_cast<SSLServerSocket*>(http_connection->socket_.get());
339 int rv = ssl_socket->Handshake(
340 base::Bind(&EmbeddedTestServer::OnHandshakeDone, base::Unretained(this),
341 http_connection));
342 if (rv != ERR_IO_PENDING)
343 OnHandshakeDone(http_connection, rv);
344 } else {
345 ReadData(http_connection);
346 }
265 } 347 }
266 348
267 void EmbeddedTestServer::ReadData(HttpConnection* connection) { 349 void EmbeddedTestServer::ReadData(HttpConnection* connection) {
268 while (true) { 350 while (true) {
269 int rv = 351 int rv =
270 connection->ReadData(base::Bind(&EmbeddedTestServer::OnReadCompleted, 352 connection->ReadData(base::Bind(&EmbeddedTestServer::OnReadCompleted,
271 base::Unretained(this), connection)); 353 base::Unretained(this), connection));
272 if (rv == ERR_IO_PENDING) 354 if (rv == ERR_IO_PENDING)
273 return; 355 return;
274 if (!HandleReadResult(connection, rv)) 356 if (!HandleReadResult(connection, rv))
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
339 run_loop.QuitClosure())) { 421 run_loop.QuitClosure())) {
340 return false; 422 return false;
341 } 423 }
342 run_loop.Run(); 424 run_loop.Run();
343 425
344 return true; 426 return true;
345 } 427 }
346 428
347 } // namespace test_server 429 } // namespace test_server
348 } // namespace net 430 } // namespace net
OLDNEW
« no previous file with comments | « net/test/embedded_test_server/embedded_test_server.h ('k') | net/test/embedded_test_server/embedded_test_server_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698