local_test_server_win.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
1// Copyright (c) 2012 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 <windows.h> 8#include <wincrypt.h> 9 10#include "base/base_paths.h" 11#include "base/bind.h" 12#include "base/command_line.h" 13#include "base/file_util.h" 14#include "base/message_loop.h" 15#include "base/path_service.h" 16#include "base/process_util.h" 17#include "base/string_number_conversions.h" 18#include "base/string_util.h" 19#include "base/test/test_timeouts.h" 20#include "base/threading/thread.h" 21#include "base/utf_string_conversions.h" 22#include "base/win/scoped_handle.h" 23#include "net/test/python_utils.h" 24 25#pragma comment(lib, "crypt32.lib") 26 27namespace { 28 29// Writes |size| bytes to |handle| and sets |*unblocked| to true. 30// Used as a crude timeout mechanism by ReadData(). 31void UnblockPipe(HANDLE handle, DWORD size, bool* unblocked) { 32 std::string unblock_data(size, '\0'); 33 // Unblock the ReadFile in LocalTestServer::WaitToStart by writing to the 34 // pipe. Make sure the call succeeded, otherwise we are very likely to hang. 35 DWORD bytes_written = 0; 36 LOG(WARNING) << "Timeout reached; unblocking pipe by writing " 37 << size << " bytes"; 38 CHECK(WriteFile(handle, unblock_data.data(), size, &bytes_written, 39 NULL)); 40 CHECK_EQ(size, bytes_written); 41 *unblocked = true; 42} 43 44// Given a file handle, reads into |buffer| until |bytes_max| bytes 45// has been read or an error has been encountered. Returns 46// true if the read was successful. 47bool ReadData(HANDLE read_fd, HANDLE write_fd, 48 DWORD bytes_max, uint8* buffer) { 49 base::Thread thread("test_server_watcher"); 50 if (!thread.Start()) 51 return false; 52 53 // Prepare a timeout in case the server fails to start. 54 bool unblocked = false; 55 thread.message_loop()->PostDelayedTask( 56 FROM_HERE, base::Bind(UnblockPipe, write_fd, bytes_max, &unblocked), 57 TestTimeouts::action_max_timeout()); 58 59 DWORD bytes_read = 0; 60 while (bytes_read < bytes_max) { 61 DWORD num_bytes; 62 if (!ReadFile(read_fd, buffer + bytes_read, bytes_max - bytes_read, 63 &num_bytes, NULL)) { 64 PLOG(ERROR) << "ReadFile failed"; 65 return false; 66 } 67 if (num_bytes <= 0) { 68 LOG(ERROR) << "ReadFile returned invalid byte count: " << num_bytes; 69 return false; 70 } 71 bytes_read += num_bytes; 72 } 73 74 thread.Stop(); 75 // If the timeout kicked in, abort. 76 if (unblocked) { 77 LOG(ERROR) << "Timeout exceeded for ReadData"; 78 return false; 79 } 80 81 return true; 82} 83 84} // namespace 85 86namespace net { 87 88bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) { 89 CommandLine python_command(CommandLine::NO_PROGRAM); 90 if (!GetPythonCommand(&python_command)) 91 return false; 92 93 python_command.AppendArgPath(testserver_path); 94 if (!AddCommandLineArguments(&python_command)) 95 return false; 96 97 HANDLE child_read = NULL; 98 HANDLE child_write = NULL; 99 if (!CreatePipe(&child_read, &child_write, NULL, 0)) { 100 PLOG(ERROR) << "Failed to create pipe"; 101 return false; 102 } 103 child_read_fd_.Set(child_read); 104 child_write_fd_.Set(child_write); 105 106 // Have the child inherit the write half. 107 if (!SetHandleInformation(child_write, HANDLE_FLAG_INHERIT, 108 HANDLE_FLAG_INHERIT)) { 109 PLOG(ERROR) << "Failed to enable pipe inheritance"; 110 return false; 111 } 112 113 // Pass the handle on the command-line. Although HANDLE is a 114 // pointer, truncating it on 64-bit machines is okay. See 115 // http://msdn.microsoft.com/en-us/library/aa384203.aspx 116 // 117 // "64-bit versions of Windows use 32-bit handles for 118 // interoperability. When sharing a handle between 32-bit and 64-bit 119 // applications, only the lower 32 bits are significant, so it is 120 // safe to truncate the handle (when passing it from 64-bit to 121 // 32-bit) or sign-extend the handle (when passing it from 32-bit to 122 // 64-bit)." 123 python_command.AppendArg("--startup-pipe=" + 124 base::IntToString(reinterpret_cast<uintptr_t>(child_write))); 125 126 job_handle_.Set(CreateJobObject(NULL, NULL)); 127 if (!job_handle_.IsValid()) { 128 LOG(ERROR) << "Could not create JobObject."; 129 return false; 130 } 131 132 if (!base::SetJobObjectAsKillOnJobClose(job_handle_.Get())) { 133 LOG(ERROR) << "Could not SetInformationJobObject."; 134 return false; 135 } 136 137 base::LaunchOptions launch_options; 138 launch_options.inherit_handles = true; 139 launch_options.job_handle = job_handle_.Get(); 140 if (!base::LaunchProcess(python_command, launch_options, &process_handle_)) { 141 LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString(); 142 return false; 143 } 144 145 return true; 146} 147 148bool LocalTestServer::WaitToStart() { 149 base::win::ScopedHandle read_fd(child_read_fd_.Take()); 150 base::win::ScopedHandle write_fd(child_write_fd_.Take()); 151 152 uint32 server_data_len = 0; 153 if (!ReadData(read_fd.Get(), write_fd.Get(), sizeof(server_data_len), 154 reinterpret_cast<uint8*>(&server_data_len))) { 155 LOG(ERROR) << "Could not read server_data_len"; 156 return false; 157 } 158 std::string server_data(server_data_len, '\0'); 159 if (!ReadData(read_fd.Get(), write_fd.Get(), server_data_len, 160 reinterpret_cast<uint8*>(&server_data[0]))) { 161 LOG(ERROR) << "Could not read server_data (" << server_data_len 162 << " bytes)"; 163 return false; 164 } 165 166 if (!ParseServerData(server_data)) { 167 LOG(ERROR) << "Could not parse server_data: " << server_data; 168 return false; 169 } 170 171 return true; 172} 173 174} // namespace net 175 176