6516136cea9d8e9bbd02fec2e8028ff0d3640f17
[platform/framework/web/crosswalk.git] / src / net / test / spawned_test_server / local_test_server.cc
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/kill.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/values.h"
14 #include "net/base/host_port_pair.h"
15 #include "net/base/net_errors.h"
16 #include "net/test/python_utils.h"
17 #include "url/gurl.h"
18
19 namespace net {
20
21 namespace {
22
23 bool AppendArgumentFromJSONValue(const std::string& key,
24                                  const base::Value& value_node,
25                                  base::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 base::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
60 LocalTestServer::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
68 LocalTestServer::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
76 LocalTestServer::~LocalTestServer() {
77   Stop();
78 }
79
80 bool 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
93 bool LocalTestServer::Start() {
94   return StartInBackground() && BlockUntilStarted();
95 }
96
97 bool 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
112 bool LocalTestServer::BlockUntilStarted() {
113   if (!WaitToStart()) {
114     Stop();
115     return false;
116   }
117
118   return SetupWhenServerStarted();
119 }
120
121 bool 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
148 bool 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
170 bool 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
201 bool LocalTestServer::AddCommandLineArguments(
202     base::CommandLine* command_line) const {
203   base::DictionaryValue arguments_dict;
204   if (!GenerateArguments(&arguments_dict))
205     return false;
206
207   // Serialize the argument dictionary into CommandLine.
208   for (base::DictionaryValue::Iterator it(arguments_dict); !it.IsAtEnd();
209        it.Advance()) {
210     const base::Value& value = it.value();
211     const std::string& key = it.key();
212
213     // Add arguments from a list.
214     if (value.IsType(base::Value::TYPE_LIST)) {
215       const base::ListValue* list = NULL;
216       if (!value.GetAsList(&list) || !list || list->empty())
217         return false;
218       for (base::ListValue::const_iterator list_it = list->begin();
219            list_it != list->end(); ++list_it) {
220         if (!AppendArgumentFromJSONValue(key, *(*list_it), command_line))
221           return false;
222       }
223     } else if (!AppendArgumentFromJSONValue(key, value, command_line)) {
224         return false;
225     }
226   }
227
228   // Append the appropriate server type argument.
229   switch (type()) {
230     case TYPE_HTTP:  // The default type is HTTP, no argument required.
231       break;
232     case TYPE_HTTPS:
233       command_line->AppendArg("--https");
234       break;
235     case TYPE_WS:
236     case TYPE_WSS:
237       command_line->AppendArg("--websocket");
238       break;
239     case TYPE_FTP:
240       command_line->AppendArg("--ftp");
241       break;
242     case TYPE_TCP_ECHO:
243       command_line->AppendArg("--tcp-echo");
244       break;
245     case TYPE_UDP_ECHO:
246       command_line->AppendArg("--udp-echo");
247       break;
248     case TYPE_BASIC_AUTH_PROXY:
249       command_line->AppendArg("--basic-auth-proxy");
250       break;
251     default:
252       NOTREACHED();
253       return false;
254   }
255
256   return true;
257 }
258
259 }  // namespace net