- add sources.
[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                                  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
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   // 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
142 bool 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
164 bool 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
195 bool 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 (base::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