Home | History | Annotate | Download | only in test
      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 
     24 namespace {
     25 
     26 bool 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().
     81 void 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.
     97 bool 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 
    137 namespace net {
    138 
    139 bool 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 
    194 bool 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