Home | History | Annotate | Download | only in spawned_test_server
      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/environment.h"
     14 #include "base/files/file_path.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/path_service.h"
     17 #include "base/process/launch.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/strings/string_util.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "base/test/test_timeouts.h"
     22 #include "base/threading/thread.h"
     23 #include "base/win/scoped_handle.h"
     24 #include "net/test/python_utils.h"
     25 
     26 #pragma comment(lib, "crypt32.lib")
     27 
     28 namespace {
     29 
     30 // Writes |size| bytes to |handle| and sets |*unblocked| to true.
     31 // Used as a crude timeout mechanism by ReadData().
     32 void UnblockPipe(HANDLE handle, DWORD size, bool* unblocked) {
     33   std::string unblock_data(size, '\0');
     34   // Unblock the ReadFile in LocalTestServer::WaitToStart by writing to the
     35   // pipe. Make sure the call succeeded, otherwise we are very likely to hang.
     36   DWORD bytes_written = 0;
     37   LOG(WARNING) << "Timeout reached; unblocking pipe by writing "
     38                << size << " bytes";
     39   CHECK(WriteFile(handle, unblock_data.data(), size, &bytes_written,
     40                   NULL));
     41   CHECK_EQ(size, bytes_written);
     42   *unblocked = true;
     43 }
     44 
     45 // Given a file handle, reads into |buffer| until |bytes_max| bytes
     46 // has been read or an error has been encountered.  Returns
     47 // true if the read was successful.
     48 bool ReadData(HANDLE read_fd, HANDLE write_fd,
     49               DWORD bytes_max, uint8* buffer) {
     50   base::Thread thread("test_server_watcher");
     51   if (!thread.Start())
     52     return false;
     53 
     54   // Prepare a timeout in case the server fails to start.
     55   bool unblocked = false;
     56   thread.message_loop()->PostDelayedTask(
     57       FROM_HERE, base::Bind(UnblockPipe, write_fd, bytes_max, &unblocked),
     58       TestTimeouts::action_max_timeout());
     59 
     60   DWORD bytes_read = 0;
     61   while (bytes_read < bytes_max) {
     62     DWORD num_bytes;
     63     if (!ReadFile(read_fd, buffer + bytes_read, bytes_max - bytes_read,
     64                   &num_bytes, NULL)) {
     65       PLOG(ERROR) << "ReadFile failed";
     66       return false;
     67     }
     68     if (num_bytes <= 0) {
     69       LOG(ERROR) << "ReadFile returned invalid byte count: " << num_bytes;
     70       return false;
     71     }
     72     bytes_read += num_bytes;
     73   }
     74 
     75   thread.Stop();
     76   // If the timeout kicked in, abort.
     77   if (unblocked) {
     78     LOG(ERROR) << "Timeout exceeded for ReadData";
     79     return false;
     80   }
     81 
     82   return true;
     83 }
     84 
     85 // Class that sets up a temporary path that includes the supplied path
     86 // at the end.
     87 //
     88 // TODO(bratell): By making this more generic we can possibly reuse
     89 //                it at other places such as
     90 //                chrome/common/multi_process_lock_unittest.cc.
     91 class ScopedPath {
     92  public:
     93   // Constructor which sets up the environment to include the path to
     94   // |path_to_add|.
     95   explicit ScopedPath(const base::FilePath& path_to_add);
     96 
     97   // Destructor that restores the path that were active when the
     98   // object was constructed.
     99   ~ScopedPath();
    100 
    101  private:
    102   // The PATH environment variable before it was changed or an empty
    103   // string if there was no PATH environment variable.
    104   std::string old_path_;
    105 
    106   // The helper object that allows us to read and set environment
    107   // variables more easily.
    108   scoped_ptr<base::Environment> environment_;
    109 
    110   // A flag saying if we have actually modified the environment.
    111   bool path_modified_;
    112 
    113   DISALLOW_COPY_AND_ASSIGN(ScopedPath);
    114 };
    115 
    116 ScopedPath::ScopedPath(const base::FilePath& path_to_add)
    117     : environment_(base::Environment::Create()),
    118       path_modified_(false) {
    119   environment_->GetVar("PATH", &old_path_);
    120 
    121   std::string new_value = old_path_;
    122   if (!new_value.empty())
    123     new_value += ";";
    124 
    125   new_value += base::WideToUTF8(path_to_add.value());
    126 
    127   path_modified_ = environment_->SetVar("PATH", new_value);
    128 }
    129 
    130 ScopedPath::~ScopedPath() {
    131   if (!path_modified_)
    132     return;
    133   if (old_path_.empty())
    134     environment_->UnSetVar("PATH");
    135   else
    136     environment_->SetVar("PATH", old_path_);
    137 }
    138 
    139 }  // namespace
    140 
    141 namespace net {
    142 
    143 bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) {
    144   base::CommandLine python_command(base::CommandLine::NO_PROGRAM);
    145   if (!GetPythonCommand(&python_command))
    146     return false;
    147 
    148   python_command.AppendArgPath(testserver_path);
    149   if (!AddCommandLineArguments(&python_command))
    150     return false;
    151 
    152   HANDLE child_read = NULL;
    153   HANDLE child_write = NULL;
    154   if (!CreatePipe(&child_read, &child_write, NULL, 0)) {
    155     PLOG(ERROR) << "Failed to create pipe";
    156     return false;
    157   }
    158   child_read_fd_.Set(child_read);
    159   child_write_fd_.Set(child_write);
    160 
    161   // Have the child inherit the write half.
    162   if (!SetHandleInformation(child_write, HANDLE_FLAG_INHERIT,
    163                             HANDLE_FLAG_INHERIT)) {
    164     PLOG(ERROR) << "Failed to enable pipe inheritance";
    165     return false;
    166   }
    167 
    168   // Pass the handle on the command-line. Although HANDLE is a
    169   // pointer, truncating it on 64-bit machines is okay. See
    170   // http://msdn.microsoft.com/en-us/library/aa384203.aspx
    171   //
    172   // "64-bit versions of Windows use 32-bit handles for
    173   // interoperability. When sharing a handle between 32-bit and 64-bit
    174   // applications, only the lower 32 bits are significant, so it is
    175   // safe to truncate the handle (when passing it from 64-bit to
    176   // 32-bit) or sign-extend the handle (when passing it from 32-bit to
    177   // 64-bit)."
    178   python_command.AppendArg("--startup-pipe=" +
    179       base::IntToString(reinterpret_cast<uintptr_t>(child_write)));
    180 
    181   job_handle_.Set(CreateJobObject(NULL, NULL));
    182   if (!job_handle_.IsValid()) {
    183     LOG(ERROR) << "Could not create JobObject.";
    184     return false;
    185   }
    186 
    187   if (!base::SetJobObjectLimitFlags(job_handle_.Get(),
    188                                     JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)) {
    189     LOG(ERROR) << "Could not SetJobObjectLimitFlags.";
    190     return false;
    191   }
    192 
    193   base::LaunchOptions launch_options;
    194   launch_options.inherit_handles = true;
    195   launch_options.job_handle = job_handle_.Get();
    196   if (!base::LaunchProcess(python_command, launch_options, &process_handle_)) {
    197     LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString();
    198     return false;
    199   }
    200 
    201   return true;
    202 }
    203 
    204 bool LocalTestServer::WaitToStart() {
    205   base::win::ScopedHandle read_fd(child_read_fd_.Take());
    206   base::win::ScopedHandle write_fd(child_write_fd_.Take());
    207 
    208   uint32 server_data_len = 0;
    209   if (!ReadData(read_fd.Get(), write_fd.Get(), sizeof(server_data_len),
    210                 reinterpret_cast<uint8*>(&server_data_len))) {
    211     LOG(ERROR) << "Could not read server_data_len";
    212     return false;
    213   }
    214   std::string server_data(server_data_len, '\0');
    215   if (!ReadData(read_fd.Get(), write_fd.Get(), server_data_len,
    216                 reinterpret_cast<uint8*>(&server_data[0]))) {
    217     LOG(ERROR) << "Could not read server_data (" << server_data_len
    218                << " bytes)";
    219     return false;
    220   }
    221 
    222   if (!ParseServerData(server_data)) {
    223     LOG(ERROR) << "Could not parse server_data: " << server_data;
    224     return false;
    225   }
    226 
    227   return true;
    228 }
    229 
    230 }  // namespace net
    231 
    232