1// Copyright (c) 2011 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/test_server.h" 6 7#include <windows.h> 8#include <wincrypt.h> 9 10#include "base/base_paths.h" 11#include "base/command_line.h" 12#include "base/file_util.h" 13#include "base/message_loop.h" 14#include "base/path_service.h" 15#include "base/string_number_conversions.h" 16#include "base/string_util.h" 17#include "base/test/test_timeouts.h" 18#include "base/threading/thread.h" 19#include "base/utf_string_conversions.h" 20#include "base/win/scoped_handle.h" 21 22#pragma comment(lib, "crypt32.lib") 23 24namespace { 25 26bool LaunchTestServerAsJob(const CommandLine& cmdline, 27 bool start_hidden, 28 base::ProcessHandle* process_handle, 29 base::win::ScopedHandle* job_handle) { 30 // Launch test server process. 31 STARTUPINFO startup_info = {0}; 32 startup_info.cb = sizeof(startup_info); 33 startup_info.dwFlags = STARTF_USESHOWWINDOW; 34 startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW; 35 PROCESS_INFORMATION process_info; 36 37 // If this code is run under a debugger, the test server process is 38 // automatically associated with a job object created by the debugger. 39 // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this. 40 if (!CreateProcess( 41 NULL, const_cast<wchar_t*>(cmdline.command_line_string().c_str()), 42 NULL, NULL, TRUE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, 43 &startup_info, &process_info)) { 44 LOG(ERROR) << "Could not create process."; 45 return false; 46 } 47 CloseHandle(process_info.hThread); 48 49 // If the caller wants the process handle, we won't close it. 50 if (process_handle) { 51 *process_handle = process_info.hProcess; 52 } else { 53 CloseHandle(process_info.hProcess); 54 } 55 56 // Create a JobObject and associate the test server process with it. 57 job_handle->Set(CreateJobObject(NULL, NULL)); 58 if (!job_handle->IsValid()) { 59 LOG(ERROR) << "Could not create JobObject."; 60 return false; 61 } else { 62 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0}; 63 limit_info.BasicLimitInformation.LimitFlags = 64 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 65 if (0 == SetInformationJobObject(job_handle->Get(), 66 JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info))) { 67 LOG(ERROR) << "Could not SetInformationJobObject."; 68 return false; 69 } 70 if (0 == AssignProcessToJobObject(job_handle->Get(), 71 process_info.hProcess)) { 72 LOG(ERROR) << "Could not AssignProcessToObject."; 73 return false; 74 } 75 } 76 return true; 77} 78 79// Writes |size| bytes to |handle| and sets |*unblocked| to true. 80// Used as a crude timeout mechanism by ReadData(). 81void UnblockPipe(HANDLE handle, DWORD size, bool* unblocked) { 82 std::string unblock_data(size, '\0'); 83 // Unblock the ReadFile in TestServer::WaitToStart by writing to the pipe. 84 // Make sure the call succeeded, otherwise we are very likely to hang. 85 DWORD bytes_written = 0; 86 LOG(WARNING) << "Timeout reached; unblocking pipe by writing " 87 << size << " bytes"; 88 CHECK(WriteFile(handle, unblock_data.data(), size, &bytes_written, 89 NULL)); 90 CHECK_EQ(size, bytes_written); 91 *unblocked = true; 92} 93 94// Given a file handle, reads into |buffer| until |bytes_max| bytes 95// has been read or an error has been encountered. Returns 96// true if the read was successful. 97bool ReadData(HANDLE read_fd, HANDLE write_fd, 98 DWORD bytes_max, uint8* buffer) { 99 base::Thread thread("test_server_watcher"); 100 if (!thread.Start()) 101 return false; 102 103 // Prepare a timeout in case the server fails to start. 104 bool unblocked = false; 105 thread.message_loop()->PostDelayedTask( 106 FROM_HERE, 107 NewRunnableFunction(UnblockPipe, write_fd, bytes_max, &unblocked), 108 TestTimeouts::action_max_timeout_ms()); 109 110 DWORD bytes_read = 0; 111 while (bytes_read < bytes_max) { 112 DWORD num_bytes; 113 if (!ReadFile(read_fd, buffer + bytes_read, bytes_max - bytes_read, 114 &num_bytes, NULL)) { 115 PLOG(ERROR) << "ReadFile failed"; 116 return false; 117 } 118 if (num_bytes <= 0) { 119 LOG(ERROR) << "ReadFile returned invalid byte count: " << num_bytes; 120 return false; 121 } 122 bytes_read += num_bytes; 123 } 124 125 thread.Stop(); 126 // If the timeout kicked in, abort. 127 if (unblocked) { 128 LOG(ERROR) << "Timeout exceeded for ReadData"; 129 return false; 130 } 131 132 return true; 133} 134 135} // namespace 136 137namespace net { 138 139bool TestServer::LaunchPython(const FilePath& testserver_path) { 140 FilePath python_exe; 141 if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_exe)) 142 return false; 143 python_exe = python_exe 144 .Append(FILE_PATH_LITERAL("third_party")) 145 .Append(FILE_PATH_LITERAL("python_26")) 146 .Append(FILE_PATH_LITERAL("python.exe")); 147 148 CommandLine python_command(python_exe); 149 python_command.AppendArgPath(testserver_path); 150 if (!AddCommandLineArguments(&python_command)) 151 return false; 152 153 HANDLE child_read = NULL; 154 HANDLE child_write = NULL; 155 if (!CreatePipe(&child_read, &child_write, NULL, 0)) { 156 PLOG(ERROR) << "Failed to create pipe"; 157 return false; 158 } 159 child_read_fd_.Set(child_read); 160 child_write_fd_.Set(child_write); 161 162 // Have the child inherit the write half. 163 if (!SetHandleInformation(child_write, HANDLE_FLAG_INHERIT, 164 HANDLE_FLAG_INHERIT)) { 165 PLOG(ERROR) << "Failed to enable pipe inheritance"; 166 return false; 167 } 168 169 // Pass the handle on the command-line. Although HANDLE is a 170 // pointer, truncating it on 64-bit machines is okay. See 171 // http://msdn.microsoft.com/en-us/library/aa384203.aspx 172 // 173 // "64-bit versions of Windows use 32-bit handles for 174 // interoperability. When sharing a handle between 32-bit and 64-bit 175 // applications, only the lower 32 bits are significant, so it is 176 // safe to truncate the handle (when passing it from 64-bit to 177 // 32-bit) or sign-extend the handle (when passing it from 32-bit to 178 // 64-bit)." 179 python_command.AppendSwitchASCII( 180 "startup-pipe", 181 base::IntToString(reinterpret_cast<uintptr_t>(child_write))); 182 183 if (!LaunchTestServerAsJob(python_command, 184 true, 185 &process_handle_, 186 &job_handle_)) { 187 LOG(ERROR) << "Failed to launch " << python_command.command_line_string(); 188 return false; 189 } 190 191 return true; 192} 193 194bool TestServer::WaitToStart() { 195 base::win::ScopedHandle read_fd(child_read_fd_.Take()); 196 base::win::ScopedHandle write_fd(child_write_fd_.Take()); 197 198 uint32 server_data_len = 0; 199 if (!ReadData(read_fd.Get(), write_fd.Get(), sizeof(server_data_len), 200 reinterpret_cast<uint8*>(&server_data_len))) { 201 LOG(ERROR) << "Could not read server_data_len"; 202 return false; 203 } 204 std::string server_data(server_data_len, '\0'); 205 if (!ReadData(read_fd.Get(), write_fd.Get(), server_data_len, 206 reinterpret_cast<uint8*>(&server_data[0]))) { 207 LOG(ERROR) << "Could not read server_data (" << server_data_len 208 << " bytes)"; 209 return false; 210 } 211 212 if (!ParseServerData(server_data)) { 213 LOG(ERROR) << "Could not parse server_data: " << server_data; 214 return false; 215 } 216 217 return true; 218} 219 220} // namespace net 221