local_test_server.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
1// Copyright 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 "net/test/spawned_test_server/local_test_server.h" 6 7#include "base/command_line.h" 8#include "base/json/json_reader.h" 9#include "base/logging.h" 10#include "base/path_service.h" 11#include "base/process_util.h" 12#include "base/string_number_conversions.h" 13#include "base/values.h" 14#include "googleurl/src/gurl.h" 15#include "net/base/host_port_pair.h" 16#include "net/base/net_errors.h" 17#include "net/test/python_utils.h" 18 19namespace net { 20 21namespace { 22 23bool AppendArgumentFromJSONValue(const std::string& key, 24 const base::Value& value_node, 25 CommandLine* command_line) { 26 std::string argument_name = "--" + key; 27 switch (value_node.GetType()) { 28 case base::Value::TYPE_NULL: 29 command_line->AppendArg(argument_name); 30 break; 31 case base::Value::TYPE_INTEGER: { 32 int value; 33 bool result = value_node.GetAsInteger(&value); 34 DCHECK(result); 35 command_line->AppendArg(argument_name + "=" + base::IntToString(value)); 36 break; 37 } 38 case Value::TYPE_STRING: { 39 std::string value; 40 bool result = value_node.GetAsString(&value); 41 if (!result || value.empty()) 42 return false; 43 command_line->AppendArg(argument_name + "=" + value); 44 break; 45 } 46 case base::Value::TYPE_BOOLEAN: 47 case base::Value::TYPE_DOUBLE: 48 case base::Value::TYPE_LIST: 49 case base::Value::TYPE_DICTIONARY: 50 case base::Value::TYPE_BINARY: 51 default: 52 NOTREACHED() << "improper json type"; 53 return false; 54 } 55 return true; 56} 57 58} // namespace 59 60LocalTestServer::LocalTestServer(Type type, 61 const std::string& host, 62 const base::FilePath& document_root) 63 : BaseTestServer(type, host) { 64 if (!Init(document_root)) 65 NOTREACHED(); 66} 67 68LocalTestServer::LocalTestServer(Type type, 69 const SSLOptions& ssl_options, 70 const base::FilePath& document_root) 71 : BaseTestServer(type, ssl_options) { 72 if (!Init(document_root)) 73 NOTREACHED(); 74} 75 76LocalTestServer::~LocalTestServer() { 77 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 // First check if the process has already terminated. 128 bool ret = base::WaitForSingleProcess(process_handle_, base::TimeDelta()); 129 if (!ret) 130 ret = base::KillProcess(process_handle_, 1, true); 131 132 if (ret) { 133 base::CloseProcessHandle(process_handle_); 134 process_handle_ = base::kNullProcessHandle; 135 } else { 136 VLOG(1) << "Kill failed?"; 137 } 138 139 return ret; 140} 141 142bool LocalTestServer::Init(const base::FilePath& document_root) { 143 if (document_root.IsAbsolute()) 144 return false; 145 146 // At this point, the port that the test server will listen on is unknown. 147 // The test server will listen on an ephemeral port, and write the port 148 // number out over a pipe that this TestServer object will read from. Once 149 // that is complete, the host port pair will contain the actual port. 150 DCHECK(!GetPort()); 151 process_handle_ = base::kNullProcessHandle; 152 153 base::FilePath src_dir; 154 if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) 155 return false; 156 SetResourcePath(src_dir.Append(document_root), 157 src_dir.AppendASCII("net") 158 .AppendASCII("data") 159 .AppendASCII("ssl") 160 .AppendASCII("certificates")); 161 return true; 162} 163 164bool LocalTestServer::SetPythonPath() const { 165 base::FilePath third_party_dir; 166 if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) { 167 LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT"; 168 return false; 169 } 170 third_party_dir = third_party_dir.AppendASCII("third_party"); 171 172 // For simplejson. (simplejson, unlike all the other Python modules 173 // we include, doesn't have an extra 'simplejson' directory, so we 174 // need to include its parent directory, i.e. third_party_dir). 175 AppendToPythonPath(third_party_dir); 176 177 AppendToPythonPath(third_party_dir.AppendASCII("tlslite")); 178 AppendToPythonPath( 179 third_party_dir.AppendASCII("pyftpdlib").AppendASCII("src")); 180 AppendToPythonPath( 181 third_party_dir.AppendASCII("pywebsocket").AppendASCII("src")); 182 183 // Locate the Python code generated by the protocol buffers compiler. 184 base::FilePath pyproto_dir; 185 if (!GetPyProtoPath(&pyproto_dir)) { 186 LOG(WARNING) << "Cannot find pyproto dir for generated code. " 187 << "Testserver features that rely on it will not work"; 188 return true; 189 } 190 AppendToPythonPath(pyproto_dir); 191 192 return true; 193} 194 195bool LocalTestServer::AddCommandLineArguments(CommandLine* command_line) const { 196 base::DictionaryValue arguments_dict; 197 if (!GenerateArguments(&arguments_dict)) 198 return false; 199 200 // Serialize the argument dictionary into CommandLine. 201 for (DictionaryValue::Iterator it(arguments_dict); !it.IsAtEnd(); 202 it.Advance()) { 203 const base::Value& value = it.value(); 204 const std::string& key = it.key(); 205 206 // Add arguments from a list. 207 if (value.IsType(Value::TYPE_LIST)) { 208 const base::ListValue* list = NULL; 209 if (!value.GetAsList(&list) || !list || list->empty()) 210 return false; 211 for (base::ListValue::const_iterator list_it = list->begin(); 212 list_it != list->end(); ++list_it) { 213 if (!AppendArgumentFromJSONValue(key, *(*list_it), command_line)) 214 return false; 215 } 216 } else if (!AppendArgumentFromJSONValue(key, value, command_line)) { 217 return false; 218 } 219 } 220 221 // Append the appropriate server type argument. 222 switch (type()) { 223 case TYPE_HTTP: // The default type is HTTP, no argument required. 224 break; 225 case TYPE_HTTPS: 226 command_line->AppendArg("--https"); 227 break; 228 case TYPE_WS: 229 case TYPE_WSS: 230 command_line->AppendArg("--websocket"); 231 break; 232 case TYPE_FTP: 233 command_line->AppendArg("--ftp"); 234 break; 235 case TYPE_TCP_ECHO: 236 command_line->AppendArg("--tcp-echo"); 237 break; 238 case TYPE_UDP_ECHO: 239 command_line->AppendArg("--udp-echo"); 240 break; 241 case TYPE_BASIC_AUTH_PROXY: 242 command_line->AppendArg("--basic-auth-proxy"); 243 break; 244 default: 245 NOTREACHED(); 246 return false; 247 } 248 249 return true; 250} 251 252} // namespace net 253