xref: /aosp_15_r20/external/cronet/net/test/spawned_test_server/local_test_server_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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