1 // Copyright 2012 The Chromium Authors
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 <windows.h>
8
9 #include "base/base_paths.h"
10 #include "base/command_line.h"
11 #include "base/environment.h"
12 #include "base/files/file_path.h"
13 #include "base/functional/bind.h"
14 #include "base/logging.h"
15 #include "base/path_service.h"
16 #include "base/process/launch.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/win/scoped_handle.h"
21 #include "net/test/python_utils.h"
22
23 namespace {
24
25 // Given a file handle, reads into |buffer| until |bytes_max| bytes
26 // has been read or an error has been encountered. Returns
27 // true if the read was successful.
ReadData(HANDLE read_fd,HANDLE write_fd,DWORD bytes_max,uint8_t * buffer)28 bool ReadData(HANDLE read_fd,
29 HANDLE write_fd,
30 DWORD bytes_max,
31 uint8_t* buffer) {
32 DWORD bytes_read = 0;
33 while (bytes_read < bytes_max) {
34 DWORD num_bytes;
35 if (!ReadFile(read_fd, buffer + bytes_read, bytes_max - bytes_read,
36 &num_bytes, nullptr)) {
37 PLOG(ERROR) << "ReadFile failed";
38 return false;
39 }
40 if (num_bytes <= 0) {
41 LOG(ERROR) << "ReadFile returned invalid byte count: " << num_bytes;
42 return false;
43 }
44 bytes_read += num_bytes;
45 }
46
47 return true;
48 }
49
50 } // namespace
51
52 namespace net {
53
LaunchPython(const base::FilePath & testserver_path,const std::vector<base::FilePath> & python_path)54 bool LocalTestServer::LaunchPython(
55 const base::FilePath& testserver_path,
56 const std::vector<base::FilePath>& python_path) {
57 base::CommandLine python_command(base::CommandLine::NO_PROGRAM);
58 if (!GetPython3Command(&python_command))
59 return false;
60
61 python_command.AppendArgPath(testserver_path);
62 if (!AddCommandLineArguments(&python_command))
63 return false;
64
65 HANDLE child_read = nullptr;
66 HANDLE child_write = nullptr;
67 if (!CreatePipe(&child_read, &child_write, nullptr, 0)) {
68 PLOG(ERROR) << "Failed to create pipe";
69 return false;
70 }
71 child_read_fd_.Set(child_read);
72 child_write_fd_.Set(child_write);
73
74 // Have the child inherit the write half.
75 if (!::DuplicateHandle(::GetCurrentProcess(), child_write,
76 ::GetCurrentProcess(), &child_write, 0, TRUE,
77 DUPLICATE_SAME_ACCESS)) {
78 PLOG(ERROR) << "Failed to enable pipe inheritance";
79 return false;
80 }
81
82 // Pass the handle on the command-line. Although HANDLE is a
83 // pointer, truncating it on 64-bit machines is okay. See
84 // http://msdn.microsoft.com/en-us/library/aa384203.aspx
85 //
86 // "64-bit versions of Windows use 32-bit handles for
87 // interoperability. When sharing a handle between 32-bit and 64-bit
88 // applications, only the lower 32 bits are significant, so it is
89 // safe to truncate the handle (when passing it from 64-bit to
90 // 32-bit) or sign-extend the handle (when passing it from 32-bit to
91 // 64-bit)."
92 python_command.AppendArg(
93 "--startup-pipe=" +
94 base::NumberToString(reinterpret_cast<uintptr_t>(child_write)));
95
96 base::LaunchOptions launch_options;
97 SetPythonPathInEnvironment(python_path, &launch_options.environment);
98
99 // Set CWD to source root.
100 if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT,
101 &launch_options.current_directory)) {
102 LOG(ERROR) << "Failed to get DIR_SRC_TEST_DATA_ROOT";
103 return false;
104 }
105
106 // TODO(brettw) bug 748258: Share only explicit handles.
107 launch_options.inherit_mode = base::LaunchOptions::Inherit::kAll;
108 process_ = base::LaunchProcess(python_command, launch_options);
109 if (!process_.IsValid()) {
110 LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString();
111 ::CloseHandle(child_write);
112 return false;
113 }
114
115 ::CloseHandle(child_write);
116 return true;
117 }
118
WaitToStart()119 bool LocalTestServer::WaitToStart() {
120 base::win::ScopedHandle read_fd(child_read_fd_.Take());
121 base::win::ScopedHandle write_fd(child_write_fd_.Take());
122
123 uint32_t server_data_len = 0;
124 if (!ReadData(read_fd.Get(), write_fd.Get(), sizeof(server_data_len),
125 reinterpret_cast<uint8_t*>(&server_data_len))) {
126 LOG(ERROR) << "Could not read server_data_len";
127 return false;
128 }
129 std::string server_data(server_data_len, '\0');
130 if (!ReadData(read_fd.Get(), write_fd.Get(), server_data_len,
131 reinterpret_cast<uint8_t*>(&server_data[0]))) {
132 LOG(ERROR) << "Could not read server_data (" << server_data_len
133 << " bytes)";
134 return false;
135 }
136
137 int port;
138 if (!SetAndParseServerData(server_data, &port)) {
139 LOG(ERROR) << "Could not parse server_data: " << server_data;
140 return false;
141 }
142 SetPort(port);
143
144 return true;
145 }
146
147 } // namespace net
148