local_test_server.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// found in the LICENSE file. 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "net/test/spawned_test_server/local_test_server.h" 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/command_line.h" 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/json/json_reader.h" 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/logging.h" 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/path_service.h" 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/process/kill.h" 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/values.h" 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "net/base/host_port_pair.h" 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "net/base/net_errors.h" 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "net/test/python_utils.h" 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "url/gurl.h" 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)namespace net { 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)namespace { 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool AppendArgumentFromJSONValue(const std::string& key, 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const base::Value& value_node, 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) CommandLine* command_line) { 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) std::string argument_name = "--" + key; 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) switch (value_node.GetType()) { 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case base::Value::TYPE_NULL: 295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) command_line->AppendArg(argument_name); 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) break; 315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case base::Value::TYPE_INTEGER: { 3253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) int value; 335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bool result = value_node.GetAsInteger(&value); 3476c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles) DCHECK(result); 3553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) command_line->AppendArg(argument_name + "=" + base::IntToString(value)); 365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) break; 37c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) } 385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case Value::TYPE_STRING: { 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) std::string value; 405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bool result = value_node.GetAsString(&value); 415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!result || value.empty()) 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return false; 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) command_line->AppendArg(argument_name + "=" + value); 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) break; 4507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case base::Value::TYPE_BOOLEAN: 47926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) case base::Value::TYPE_DOUBLE: 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case base::Value::TYPE_LIST: 49926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) case base::Value::TYPE_DICTIONARY: 505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case base::Value::TYPE_BINARY: 515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) default: 5276c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles) NOTREACHED() << "improper json type"; 535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return false; 54d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) } 555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return true; 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} // namespace 5976c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles) 605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)LocalTestServer::LocalTestServer(Type type, 615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const std::string& host, 6207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch const base::FilePath& document_root) 635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) : BaseTestServer(type, host) { 645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!Init(document_root)) 6576c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles) NOTREACHED(); 665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 6707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch 68d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)LocalTestServer::LocalTestServer(Type type, 695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const SSLOptions& ssl_options, 705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const base::FilePath& document_root) 7107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch : BaseTestServer(type, ssl_options) { 7207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (!Init(document_root)) 7307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch NOTREACHED(); 7476c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles)} 7507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch 7607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben MurdochLocalTestServer::~LocalTestServer() { 775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Stop(); 78} 79 80bool LocalTestServer::GetTestServerPath(base::FilePath* testserver_path) const { 81 base::FilePath testserver_dir; 82 if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_dir)) { 83 LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT"; 84 return false; 85 } 86 testserver_dir = testserver_dir.Append(FILE_PATH_LITERAL("net")) 87 .Append(FILE_PATH_LITERAL("tools")) 88 .Append(FILE_PATH_LITERAL("testserver")); 89 *testserver_path = testserver_dir.Append(FILE_PATH_LITERAL("testserver.py")); 90 return true; 91} 92 93bool LocalTestServer::Start() { 94 return StartInBackground() && BlockUntilStarted(); 95} 96 97bool LocalTestServer::StartInBackground() { 98 // Get path to Python server script. 99 base::FilePath testserver_path; 100 if (!GetTestServerPath(&testserver_path)) 101 return false; 102 103 if (!SetPythonPath()) 104 return false; 105 106 if (!LaunchPython(testserver_path)) 107 return false; 108 109 return true; 110} 111 112bool LocalTestServer::BlockUntilStarted() { 113 if (!WaitToStart()) { 114 Stop(); 115 return false; 116 } 117 118 return SetupWhenServerStarted(); 119} 120 121bool LocalTestServer::Stop() { 122 CleanUpWhenStoppingServer(); 123 124 if (!process_handle_) 125 return true; 126 127#if defined(OS_WIN) 128 // This kills all the processes in the job object. 129 job_handle_.Close(); 130#endif 131 132 // First check if the process has already terminated. 133 bool ret = base::WaitForSingleProcess(process_handle_, base::TimeDelta()); 134 if (!ret) { 135 ret = base::KillProcess(process_handle_, 1, true); 136 } 137 138 if (ret) { 139 base::CloseProcessHandle(process_handle_); 140 process_handle_ = base::kNullProcessHandle; 141 } else { 142 VLOG(1) << "Kill failed?"; 143 } 144 145 return ret; 146} 147 148bool LocalTestServer::Init(const base::FilePath& document_root) { 149 if (document_root.IsAbsolute()) 150 return false; 151 152 // At this point, the port that the test server will listen on is unknown. 153 // The test server will listen on an ephemeral port, and write the port 154 // number out over a pipe that this TestServer object will read from. Once 155 // that is complete, the host port pair will contain the actual port. 156 DCHECK(!GetPort()); 157 process_handle_ = base::kNullProcessHandle; 158 159 base::FilePath src_dir; 160 if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) 161 return false; 162 SetResourcePath(src_dir.Append(document_root), 163 src_dir.AppendASCII("net") 164 .AppendASCII("data") 165 .AppendASCII("ssl") 166 .AppendASCII("certificates")); 167 return true; 168} 169 170bool LocalTestServer::SetPythonPath() const { 171 base::FilePath third_party_dir; 172 if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) { 173 LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT"; 174 return false; 175 } 176 third_party_dir = third_party_dir.AppendASCII("third_party"); 177 178 // For simplejson. (simplejson, unlike all the other Python modules 179 // we include, doesn't have an extra 'simplejson' directory, so we 180 // need to include its parent directory, i.e. third_party_dir). 181 AppendToPythonPath(third_party_dir); 182 183 AppendToPythonPath(third_party_dir.AppendASCII("tlslite")); 184 AppendToPythonPath( 185 third_party_dir.AppendASCII("pyftpdlib").AppendASCII("src")); 186 AppendToPythonPath( 187 third_party_dir.AppendASCII("pywebsocket").AppendASCII("src")); 188 189 // Locate the Python code generated by the protocol buffers compiler. 190 base::FilePath pyproto_dir; 191 if (!GetPyProtoPath(&pyproto_dir)) { 192 LOG(WARNING) << "Cannot find pyproto dir for generated code. " 193 << "Testserver features that rely on it will not work"; 194 return true; 195 } 196 AppendToPythonPath(pyproto_dir); 197 198 return true; 199} 200 201bool LocalTestServer::AddCommandLineArguments(CommandLine* command_line) const { 202 base::DictionaryValue arguments_dict; 203 if (!GenerateArguments(&arguments_dict)) 204 return false; 205 206 // Serialize the argument dictionary into CommandLine. 207 for (base::DictionaryValue::Iterator it(arguments_dict); !it.IsAtEnd(); 208 it.Advance()) { 209 const base::Value& value = it.value(); 210 const std::string& key = it.key(); 211 212 // Add arguments from a list. 213 if (value.IsType(Value::TYPE_LIST)) { 214 const base::ListValue* list = NULL; 215 if (!value.GetAsList(&list) || !list || list->empty()) 216 return false; 217 for (base::ListValue::const_iterator list_it = list->begin(); 218 list_it != list->end(); ++list_it) { 219 if (!AppendArgumentFromJSONValue(key, *(*list_it), command_line)) 220 return false; 221 } 222 } else if (!AppendArgumentFromJSONValue(key, value, command_line)) { 223 return false; 224 } 225 } 226 227 // Append the appropriate server type argument. 228 switch (type()) { 229 case TYPE_HTTP: // The default type is HTTP, no argument required. 230 break; 231 case TYPE_HTTPS: 232 command_line->AppendArg("--https"); 233 break; 234 case TYPE_WS: 235 case TYPE_WSS: 236 command_line->AppendArg("--websocket"); 237 break; 238 case TYPE_FTP: 239 command_line->AppendArg("--ftp"); 240 break; 241 case TYPE_TCP_ECHO: 242 command_line->AppendArg("--tcp-echo"); 243 break; 244 case TYPE_UDP_ECHO: 245 command_line->AppendArg("--udp-echo"); 246 break; 247 case TYPE_BASIC_AUTH_PROXY: 248 command_line->AppendArg("--basic-auth-proxy"); 249 break; 250 default: 251 NOTREACHED(); 252 return false; 253 } 254 255 return true; 256} 257 258} // namespace net 259