test_server_win.cc revision a8d393aaf9718c42d29078685fc0b94add05b863
1// Copyright (c) 2010 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/file_util.h"
12#include "base/path_service.h"
13#include "base/string_number_conversions.h"
14#include "base/string_util.h"
15#include "base/utf_string_conversions.h"
16
17#pragma comment(lib, "crypt32.lib")
18
19namespace {
20
21bool LaunchTestServerAsJob(const std::wstring& cmdline,
22                           bool start_hidden,
23                           base::ProcessHandle* process_handle,
24                           ScopedHandle* job_handle) {
25  // Launch test server process.
26  STARTUPINFO startup_info = {0};
27  startup_info.cb = sizeof(startup_info);
28  startup_info.dwFlags = STARTF_USESHOWWINDOW;
29  startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW;
30  PROCESS_INFORMATION process_info;
31
32  // If this code is run under a debugger, the test server process is
33  // automatically associated with a job object created by the debugger.
34  // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this.
35  if (!CreateProcess(NULL,
36                     const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
37                     TRUE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL,
38                     &startup_info, &process_info)) {
39    LOG(ERROR) << "Could not create process.";
40    return false;
41  }
42  CloseHandle(process_info.hThread);
43
44  // If the caller wants the process handle, we won't close it.
45  if (process_handle) {
46    *process_handle = process_info.hProcess;
47  } else {
48    CloseHandle(process_info.hProcess);
49  }
50
51  // Create a JobObject and associate the test server process with it.
52  job_handle->Set(CreateJobObject(NULL, NULL));
53  if (!job_handle->IsValid()) {
54    LOG(ERROR) << "Could not create JobObject.";
55    return false;
56  } else {
57    JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
58    limit_info.BasicLimitInformation.LimitFlags =
59        JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
60    if (0 == SetInformationJobObject(job_handle->Get(),
61      JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info))) {
62      LOG(ERROR) << "Could not SetInformationJobObject.";
63      return false;
64    }
65    if (0 == AssignProcessToJobObject(job_handle->Get(),
66                                      process_info.hProcess)) {
67      LOG(ERROR) << "Could not AssignProcessToObject.";
68      return false;
69    }
70  }
71  return true;
72}
73
74}  // namespace
75
76namespace net {
77bool TestServer::LaunchPython(const FilePath& testserver_path) {
78  FilePath python_exe;
79  if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_exe))
80    return false;
81  python_exe = python_exe
82      .Append(FILE_PATH_LITERAL("third_party"))
83      .Append(FILE_PATH_LITERAL("python_24"))
84      .Append(FILE_PATH_LITERAL("python.exe"));
85
86  std::wstring command_line =
87      L"\"" + python_exe.value() + L"\" " +
88      L"\"" + testserver_path.value() +
89      L"\" --port=" + ASCIIToWide(base::IntToString(host_port_pair_.port())) +
90      L" --data-dir=\"" + document_root_.value() + L"\"";
91
92  if (type_ == TYPE_FTP)
93    command_line.append(L" -f");
94
95  FilePath certificate_path(GetCertificatePath());
96  if (!certificate_path.value().empty()) {
97    if (!file_util::PathExists(certificate_path)) {
98      LOG(ERROR) << "Certificate path " << certificate_path.value()
99                 << " doesn't exist. Can't launch https server.";
100      return false;
101    }
102    command_line.append(L" --https=\"");
103    command_line.append(certificate_path.value());
104    command_line.append(L"\"");
105  }
106
107  if (type_ == TYPE_HTTPS_CLIENT_AUTH)
108    command_line.append(L" --ssl-client-auth");
109
110  HANDLE child_read = NULL;
111  HANDLE child_write = NULL;
112  if (!CreatePipe(&child_read, &child_write, NULL, 0)) {
113    PLOG(ERROR) << "Failed to create pipe";
114    return false;
115  }
116  child_fd_.Set(child_read);
117  ScopedHandle scoped_child_write(child_write);
118
119  // Have the child inherit the write half.
120  if (!SetHandleInformation(child_write, HANDLE_FLAG_INHERIT,
121                            HANDLE_FLAG_INHERIT)) {
122    PLOG(ERROR) << "Failed to enable pipe inheritance";
123    return false;
124  }
125
126  // Pass the handle on the command-line. Although HANDLE is a
127  // pointer, truncating it on 64-bit machines is okay. See
128  // http://msdn.microsoft.com/en-us/library/aa384203.aspx
129  //
130  // "64-bit versions of Windows use 32-bit handles for
131  // interoperability. When sharing a handle between 32-bit and 64-bit
132  // applications, only the lower 32 bits are significant, so it is
133  // safe to truncate the handle (when passing it from 64-bit to
134  // 32-bit) or sign-extend the handle (when passing it from 32-bit to
135  // 64-bit)."
136  command_line.append(
137      L" --startup-pipe=" +
138      ASCIIToWide(base::IntToString(reinterpret_cast<uintptr_t>(child_write))));
139
140  if (!LaunchTestServerAsJob(command_line,
141                             true,
142                             &process_handle_,
143                             &job_handle_)) {
144    LOG(ERROR) << "Failed to launch " << command_line;
145    return false;
146  }
147
148  return true;
149}
150
151bool TestServer::WaitToStart() {
152  char buf[8];
153  DWORD bytes_read;
154  BOOL result = ReadFile(child_fd_, buf, sizeof(buf), &bytes_read, NULL);
155  child_fd_.Close();
156  return result && bytes_read > 0;
157}
158
159bool TestServer::CheckCATrusted() {
160  HCERTSTORE cert_store = CertOpenSystemStore(NULL, L"ROOT");
161  if (!cert_store) {
162    LOG(ERROR) << " could not open trusted root CA store";
163    return false;
164  }
165  PCCERT_CONTEXT cert =
166      CertFindCertificateInStore(cert_store,
167                                 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
168                                 0,
169                                 CERT_FIND_ISSUER_STR,
170                                 L"Test CA",
171                                 NULL);
172  if (cert)
173    CertFreeCertificateContext(cert);
174  CertCloseStore(cert_store, 0);
175
176  if (!cert) {
177    LOG(ERROR) << " TEST CONFIGURATION ERROR: you need to import the test ca "
178                  "certificate to your trusted roots for this test to work. "
179                  "For more info visit:\n"
180                  "http://dev.chromium.org/developers/testing\n";
181    return false;
182  }
183
184  return true;
185}
186
187}  // namespace net
188