13f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen// Use of this source code is governed by a BSD-style license that can be 3a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen// found in the LICENSE file. 4a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 5a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#include "net/test/test_server.h" 6a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 7a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#include <windows.h> 8a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#include <wincrypt.h> 9a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 10a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#include "base/base_paths.h" 11513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "base/command_line.h" 12a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#include "base/file_util.h" 13513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "base/message_loop.h" 14a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#include "base/path_service.h" 15a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#include "base/string_number_conversions.h" 16a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#include "base/string_util.h" 17513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "base/test/test_timeouts.h" 183f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread.h" 19a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#include "base/utf_string_conversions.h" 203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/win/scoped_handle.h" 21a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 22a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen#pragma comment(lib, "crypt32.lib") 23a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 24a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsennamespace { 25a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 26513209b27ff55e2841eac0e4120199c23acce758Ben Murdochbool LaunchTestServerAsJob(const CommandLine& cmdline, 27a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen bool start_hidden, 28a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen base::ProcessHandle* process_handle, 293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen base::win::ScopedHandle* job_handle) { 30a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // Launch test server process. 31a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen STARTUPINFO startup_info = {0}; 32a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen startup_info.cb = sizeof(startup_info); 33a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen startup_info.dwFlags = STARTF_USESHOWWINDOW; 34a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW; 35a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen PROCESS_INFORMATION process_info; 36a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 37a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // If this code is run under a debugger, the test server process is 38a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // automatically associated with a job object created by the debugger. 39a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this. 40513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!CreateProcess( 41513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch NULL, const_cast<wchar_t*>(cmdline.command_line_string().c_str()), 42513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch NULL, NULL, TRUE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, 43513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch &startup_info, &process_info)) { 44a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen LOG(ERROR) << "Could not create process."; 45a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return false; 46a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } 47a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen CloseHandle(process_info.hThread); 48a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 49a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // If the caller wants the process handle, we won't close it. 50a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen if (process_handle) { 51a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen *process_handle = process_info.hProcess; 52a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } else { 53a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen CloseHandle(process_info.hProcess); 54a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } 55a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 56a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // Create a JobObject and associate the test server process with it. 57a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen job_handle->Set(CreateJobObject(NULL, NULL)); 58a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen if (!job_handle->IsValid()) { 59a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen LOG(ERROR) << "Could not create JobObject."; 60a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return false; 61a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } else { 62a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0}; 63a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen limit_info.BasicLimitInformation.LimitFlags = 64a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 65a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen if (0 == SetInformationJobObject(job_handle->Get(), 66a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info))) { 67a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen LOG(ERROR) << "Could not SetInformationJobObject."; 68a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return false; 69a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } 70a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen if (0 == AssignProcessToJobObject(job_handle->Get(), 71a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen process_info.hProcess)) { 72a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen LOG(ERROR) << "Could not AssignProcessToObject."; 73a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return false; 74a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } 75a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } 76a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return true; 77a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen} 78a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 79201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// Writes |size| bytes to |handle| and sets |*unblocked| to true. 80201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// Used as a crude timeout mechanism by ReadData(). 81201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid UnblockPipe(HANDLE handle, DWORD size, bool* unblocked) { 82201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch std::string unblock_data(size, '\0'); 83513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch // Unblock the ReadFile in TestServer::WaitToStart by writing to the pipe. 84513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch // Make sure the call succeeded, otherwise we are very likely to hang. 85513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch DWORD bytes_written = 0; 86201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch LOG(WARNING) << "Timeout reached; unblocking pipe by writing " 87201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch << size << " bytes"; 88201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch CHECK(WriteFile(handle, unblock_data.data(), size, &bytes_written, 89513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch NULL)); 90201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch CHECK_EQ(size, bytes_written); 91513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch *unblocked = true; 92513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 93513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 94201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// Given a file handle, reads into |buffer| until |bytes_max| bytes 95201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// has been read or an error has been encountered. Returns 96201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// true if the read was successful. 97201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochbool ReadData(HANDLE read_fd, HANDLE write_fd, 98201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DWORD bytes_max, uint8* buffer) { 99201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch base::Thread thread("test_server_watcher"); 100201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (!thread.Start()) 101201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return false; 102201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 103201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Prepare a timeout in case the server fails to start. 104201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch bool unblocked = false; 105201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch thread.message_loop()->PostDelayedTask( 106201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch FROM_HERE, 107201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch NewRunnableFunction(UnblockPipe, write_fd, bytes_max, &unblocked), 108201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch TestTimeouts::action_max_timeout_ms()); 109201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 110201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DWORD bytes_read = 0; 111201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch while (bytes_read < bytes_max) { 112201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DWORD num_bytes; 113201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (!ReadFile(read_fd, buffer + bytes_read, bytes_max - bytes_read, 114201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch &num_bytes, NULL)) { 115201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch PLOG(ERROR) << "ReadFile failed"; 116201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return false; 117201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 118201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (num_bytes <= 0) { 119201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch LOG(ERROR) << "ReadFile returned invalid byte count: " << num_bytes; 120201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return false; 121201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 122201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch bytes_read += num_bytes; 123201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 124201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 125201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch thread.Stop(); 126201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // If the timeout kicked in, abort. 127201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (unblocked) { 128201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch LOG(ERROR) << "Timeout exceeded for ReadData"; 129201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return false; 130201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 131201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 132201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return true; 133201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch} 134201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 135a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen} // namespace 136a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 137a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsennamespace net { 138513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 139a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsenbool TestServer::LaunchPython(const FilePath& testserver_path) { 140a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen FilePath python_exe; 141a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_exe)) 142a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return false; 143a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen python_exe = python_exe 144a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen .Append(FILE_PATH_LITERAL("third_party")) 145201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch .Append(FILE_PATH_LITERAL("python_26")) 146a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen .Append(FILE_PATH_LITERAL("python.exe")); 147a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 148513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch CommandLine python_command(python_exe); 149513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch python_command.AppendArgPath(testserver_path); 150513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!AddCommandLineArguments(&python_command)) 151513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch return false; 152a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 153a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen HANDLE child_read = NULL; 154a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen HANDLE child_write = NULL; 155a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen if (!CreatePipe(&child_read, &child_write, NULL, 0)) { 156a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen PLOG(ERROR) << "Failed to create pipe"; 157a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return false; 158a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } 159513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch child_read_fd_.Set(child_read); 160513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch child_write_fd_.Set(child_write); 161a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 162a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // Have the child inherit the write half. 163a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen if (!SetHandleInformation(child_write, HANDLE_FLAG_INHERIT, 164a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen HANDLE_FLAG_INHERIT)) { 165a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen PLOG(ERROR) << "Failed to enable pipe inheritance"; 166a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return false; 167a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } 168a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 169a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // Pass the handle on the command-line. Although HANDLE is a 170a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // pointer, truncating it on 64-bit machines is okay. See 171a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // http://msdn.microsoft.com/en-us/library/aa384203.aspx 172a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // 173a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // "64-bit versions of Windows use 32-bit handles for 174a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // interoperability. When sharing a handle between 32-bit and 64-bit 175a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // applications, only the lower 32 bits are significant, so it is 176a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // safe to truncate the handle (when passing it from 64-bit to 177a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // 32-bit) or sign-extend the handle (when passing it from 32-bit to 178a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen // 64-bit)." 179513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch python_command.AppendSwitchASCII( 180513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch "startup-pipe", 181513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch base::IntToString(reinterpret_cast<uintptr_t>(child_write))); 182a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 183513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!LaunchTestServerAsJob(python_command, 184a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen true, 185a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen &process_handle_, 186a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen &job_handle_)) { 187513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch LOG(ERROR) << "Failed to launch " << python_command.command_line_string(); 188a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return false; 189a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen } 190a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 191a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen return true; 192a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen} 193a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 194a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsenbool TestServer::WaitToStart() { 1953f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen base::win::ScopedHandle read_fd(child_read_fd_.Take()); 1963f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen base::win::ScopedHandle write_fd(child_write_fd_.Take()); 197513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 198201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch uint32 server_data_len = 0; 199201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (!ReadData(read_fd.Get(), write_fd.Get(), sizeof(server_data_len), 200201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch reinterpret_cast<uint8*>(&server_data_len))) { 201201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch LOG(ERROR) << "Could not read server_data_len"; 202201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return false; 2034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch } 204201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch std::string server_data(server_data_len, '\0'); 205201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (!ReadData(read_fd.Get(), write_fd.Get(), server_data_len, 206201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch reinterpret_cast<uint8*>(&server_data[0]))) { 207201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch LOG(ERROR) << "Could not read server_data (" << server_data_len 208201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch << " bytes)"; 209513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch return false; 210201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 211513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 212201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (!ParseServerData(server_data)) { 213201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch LOG(ERROR) << "Could not parse server_data: " << server_data; 2144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return false; 215201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 2164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return true; 218a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen} 219a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen 220a8d393aaf9718c42d29078685fc0b94add05b863Kristian Monsen} // namespace net 221