summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-07 23:00:07 +0000
committermattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-07 23:00:07 +0000
commitaa094a63e9c32261902648b380e52e8436227cea (patch)
treeb22d0a67f3b315f2c242e0388e87d5caad7eba7b
parentda48400647dbc5bf743c8ef75c364b567b49898e (diff)
downloadchromium_src-aa094a63e9c32261902648b380e52e8436227cea.zip
chromium_src-aa094a63e9c32261902648b380e52e8436227cea.tar.gz
chromium_src-aa094a63e9c32261902648b380e52e8436227cea.tar.bz2
Try #2.1: Run safebrowsing_service_test through the net testserver code.
Allows us to use ephemeral ports. (Relanding the change but with the test still disabled.) 1st review: https://chromiumcodereview.appspot.com/10073033 2nd review: https://chromiumcodereview.appspot.com/10918251 BUG=96459,119403 TBR=rsleevi@chromium.org Review URL: https://chromiumcodereview.appspot.com/11382002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166540 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/safe_browsing/local_safebrowsing_test_server.cc61
-rw-r--r--chrome/browser/safe_browsing/local_safebrowsing_test_server.h38
-rw-r--r--chrome/browser/safe_browsing/safe_browsing_test.cc203
-rwxr-xr-xchrome/browser/safe_browsing/safe_browsing_testserver.py47
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--net/test/base_test_server.cc6
-rw-r--r--net/test/base_test_server.h8
-rw-r--r--net/test/local_sync_test_server.cc3
-rw-r--r--net/test/local_test_server.cc18
-rw-r--r--net/test/local_test_server.h15
-rw-r--r--net/tools/testserver/run_testserver.cc2
-rwxr-xr-xnet/tools/testserver/testserver.py620
-rw-r--r--net/tools/testserver/testserver_base.py126
13 files changed, 649 insertions, 499 deletions
diff --git a/chrome/browser/safe_browsing/local_safebrowsing_test_server.cc b/chrome/browser/safe_browsing/local_safebrowsing_test_server.cc
new file mode 100644
index 0000000..c25e4d7
--- /dev/null
+++ b/chrome/browser/safe_browsing/local_safebrowsing_test_server.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/local_safebrowsing_test_server.h"
+
+#include "base/command_line.h"
+#include "base/path_service.h"
+#include "base/string_number_conversions.h"
+#include "base/values.h"
+#include "net/test/python_utils.h"
+#include "net/test/test_server.h"
+
+LocalSafeBrowsingTestServer::LocalSafeBrowsingTestServer(
+ const FilePath& data_file)
+ : net::LocalTestServer(net::TestServer::TYPE_HTTP,
+ net::TestServer::kLocalhost,
+ FilePath()),
+ data_file_(data_file) {
+}
+
+LocalSafeBrowsingTestServer::~LocalSafeBrowsingTestServer() {}
+
+bool LocalSafeBrowsingTestServer::GetTestServerPath(
+ FilePath* testserver_path) const {
+ FilePath testserver_dir;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_dir)) {
+ LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
+ return false;
+ }
+
+ testserver_dir = testserver_dir
+ .Append(FILE_PATH_LITERAL("chrome"))
+ .Append(FILE_PATH_LITERAL("browser"))
+ .Append(FILE_PATH_LITERAL("safe_browsing"));
+
+ *testserver_path = testserver_dir.Append(FILE_PATH_LITERAL(
+ "safe_browsing_testserver.py"));
+ return true;
+}
+
+bool LocalSafeBrowsingTestServer::SetPythonPath() const {
+ if (!net::LocalTestServer::SetPythonPath())
+ return false;
+
+ // Locate the Python code generated by the protocol buffers compiler.
+ FilePath pyproto_dir;
+ if (!GetPyProtoPath(&pyproto_dir)) {
+ LOG(ERROR) << "Cannot find pyproto dir for generated code.";
+ return false;
+ }
+
+ AppendToPythonPath(pyproto_dir.AppendASCII("google"));
+ return true;
+}
+
+bool LocalSafeBrowsingTestServer::GenerateAdditionalArguments(
+ base::DictionaryValue* arguments) const {
+ arguments->SetString("data-file", data_file_.value());
+ return true;
+}
diff --git a/chrome/browser/safe_browsing/local_safebrowsing_test_server.h b/chrome/browser/safe_browsing/local_safebrowsing_test_server.h
new file mode 100644
index 0000000..13d8baf
--- /dev/null
+++ b/chrome/browser/safe_browsing/local_safebrowsing_test_server.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SAFE_BROWSING_LOCAL_SAFEBROWSING_TEST_SERVER_H_
+#define CHROME_BROWSER_SAFE_BROWSING_LOCAL_SAFEBROWSING_TEST_SERVER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "net/test/local_test_server.h"
+
+// Runs a Python-based safebrowsing test server on the same machine in which the
+// LocalSafeBrowsingTestServer runs.
+class LocalSafeBrowsingTestServer : public net::LocalTestServer {
+ public:
+ // Initialize a safebrowsing server using the given |data_file|.
+ explicit LocalSafeBrowsingTestServer(const FilePath& data_file);
+
+ virtual ~LocalSafeBrowsingTestServer();
+
+ virtual bool SetPythonPath() const OVERRIDE;
+
+ // Returns the path to safe_browsing_testserver.py.
+ virtual bool GetTestServerPath(FilePath* testserver_path) const OVERRIDE;
+
+ protected:
+ // Adds the --data-file switch. Returns true on success.
+ virtual bool GenerateAdditionalArguments(
+ base::DictionaryValue* arguments) const OVERRIDE;
+
+ private:
+ FilePath data_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalSafeBrowsingTestServer);
+};
+
+#endif // CHROME_BROWSER_SAFE_BROWSING_LOCAL_SAFEBROWSING_TEST_SERVER_H_
diff --git a/chrome/browser/safe_browsing/safe_browsing_test.cc b/chrome/browser/safe_browsing/safe_browsing_test.cc
index 38b5ce5..2ab9c41 100644
--- a/chrome/browser/safe_browsing/safe_browsing_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_test.cc
@@ -32,6 +32,7 @@
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/local_safebrowsing_test_server.h"
#include "chrome/browser/safe_browsing/protocol_manager.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/ui/browser.h"
@@ -57,10 +58,9 @@ namespace {
const FilePath::CharType kDataFile[] =
FILE_PATH_LITERAL("testing_input_nomac.dat");
-const char kUrlVerifyPath[] = "/safebrowsing/verify_urls";
-const char kDBVerifyPath[] = "/safebrowsing/verify_database";
-const char kDBResetPath[] = "/reset";
-const char kTestCompletePath[] = "/test_complete";
+const char kUrlVerifyPath[] = "safebrowsing/verify_urls";
+const char kDBVerifyPath[] = "safebrowsing/verify_database";
+const char kTestCompletePath[] = "test_complete";
struct PhishingUrl {
std::string url;
@@ -110,100 +110,6 @@ bool ParsePhishingUrls(const std::string& data,
} // namespace
-class SafeBrowsingTestServer {
- public:
- explicit SafeBrowsingTestServer(const FilePath& datafile)
- : datafile_(datafile),
- server_handle_(base::kNullProcessHandle) {
- }
-
- ~SafeBrowsingTestServer() {
- EXPECT_EQ(base::kNullProcessHandle, server_handle_);
- }
-
- // Start the python server test suite.
- bool Start() {
- // Get path to python server script
- FilePath testserver_path;
- if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_path)) {
- LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
- return false;
- }
- testserver_path = testserver_path
- .Append(FILE_PATH_LITERAL("third_party"))
- .Append(FILE_PATH_LITERAL("safe_browsing"))
- .Append(FILE_PATH_LITERAL("testing"));
- AppendToPythonPath(testserver_path);
- FilePath testserver = testserver_path.Append(
- FILE_PATH_LITERAL("safebrowsing_test_server.py"));
-
- FilePath pyproto_code_dir;
- if (!GetPyProtoPath(&pyproto_code_dir)) {
- LOG(ERROR) << "Failed to get generated python protobuf dir";
- return false;
- }
- AppendToPythonPath(pyproto_code_dir);
- pyproto_code_dir = pyproto_code_dir.Append(FILE_PATH_LITERAL("google"));
- AppendToPythonPath(pyproto_code_dir);
-
- CommandLine cmd_line(CommandLine::NO_PROGRAM);
- EXPECT_TRUE(GetPythonCommand(&cmd_line));
-
- FilePath datafile = testserver_path.Append(datafile_);
- cmd_line.AppendArgPath(testserver);
- cmd_line.AppendArg(base::StringPrintf("--port=%d", kPort_));
- cmd_line.AppendArgNative(FILE_PATH_LITERAL("--datafile=") +
- datafile.value());
-
- base::LaunchOptions options;
-#if defined(OS_WIN)
- options.start_hidden = true;
-#endif
- if (!base::LaunchProcess(cmd_line, options, &server_handle_)) {
- LOG(ERROR) << "Failed to launch server: "
- << cmd_line.GetCommandLineString();
- return false;
- }
- return true;
- }
-
- // Stop the python server test suite.
- bool Stop() {
- if (server_handle_ == base::kNullProcessHandle)
- return true;
-
- // First check if the process has already terminated.
- if (!base::WaitForSingleProcess(server_handle_, base::TimeDelta()) &&
- !base::KillProcess(server_handle_, 1, true)) {
- VLOG(1) << "Kill failed?";
- return false;
- }
-
- base::CloseProcessHandle(server_handle_);
- server_handle_ = base::kNullProcessHandle;
- VLOG(1) << "Stopped.";
- return true;
- }
-
- static const char* Host() {
- return kHost_;
- }
-
- static int Port() {
- return kPort_;
- }
-
- private:
- static const char kHost_[];
- static const int kPort_;
- FilePath datafile_;
- base::ProcessHandle server_handle_;
- DISALLOW_COPY_AND_ASSIGN(SafeBrowsingTestServer);
-};
-
-const char SafeBrowsingTestServer::kHost_[] = "localhost";
-const int SafeBrowsingTestServer::kPort_ = 40102;
-
// This starts the browser and keeps status of states related to SafeBrowsing.
class SafeBrowsingServiceTest : public InProcessBrowserTest {
public:
@@ -297,13 +203,28 @@ class SafeBrowsingServiceTest : public InProcessBrowserTest {
return safe_browsing_service_->safe_browsing_thread_->message_loop();
}
+ const net::TestServer& test_server() const {
+ return *test_server_;
+ }
+
protected:
bool InitSafeBrowsingService() {
safe_browsing_service_ = g_browser_process->safe_browsing_service();
return safe_browsing_service_ != NULL;
}
- virtual void SetUpCommandLine(CommandLine* command_line) {
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ FilePath datafile_path;
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &datafile_path));
+
+ datafile_path = datafile_path.Append(FILE_PATH_LITERAL("third_party"))
+ .Append(FILE_PATH_LITERAL("safe_browsing"))
+ .Append(FILE_PATH_LITERAL("testing"))
+ .Append(kDataFile);
+ test_server_.reset(new LocalSafeBrowsingTestServer(datafile_path));
+ ASSERT_TRUE(test_server_->Start());
+ LOG(INFO) << "server is " << test_server_->host_port_pair().ToString();
+
// Makes sure the auto update is not triggered. This test will force the
// update when needed.
command_line->AppendSwitch(switches::kSbDisableAutoUpdate);
@@ -323,10 +244,7 @@ class SafeBrowsingServiceTest : public InProcessBrowserTest {
switches::kDisableClientSidePhishingDetection);
// Point to the testing server for all SafeBrowsing requests.
- std::string url_prefix =
- base::StringPrintf("http://%s:%d/safebrowsing",
- SafeBrowsingTestServer::Host(),
- SafeBrowsingTestServer::Port());
+ std::string url_prefix = test_server_->GetURL("safebrowsing").spec();
command_line->AppendSwitchASCII(switches::kSbURLPrefix, url_prefix);
}
@@ -338,6 +256,8 @@ class SafeBrowsingServiceTest : public InProcessBrowserTest {
private:
SafeBrowsingService* safe_browsing_service_;
+ scoped_ptr<net::TestServer> test_server_;
+
// Protects all variables below since they are read on UI thread
// but updated on IO thread or safebrowsing thread.
base::Lock update_status_mutex_;
@@ -443,52 +363,36 @@ class SafeBrowsingServiceTestHelper
content::RunMessageLoop();
}
- void WaitTillServerReady(const char* host, int port) {
- response_status_ = net::URLRequestStatus::FAILED;
- GURL url(base::StringPrintf("http://%s:%d%s?test_step=0",
- host, port, kDBResetPath));
- // TODO(lzheng): We should have a way to reliably tell when a server is
- // ready so we could get rid of the Sleep and retry loop.
- while (true) {
- if (FetchUrl(url) == net::URLRequestStatus::SUCCESS)
- break;
- // Wait and try again if last fetch was failed. The loop will hit the
- // timeout in OutOfProcTestRunner if the fetch can not get success
- // response.
- base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
- }
- }
-
// Calls test server to fetch database for verification.
- net::URLRequestStatus::Status FetchDBToVerify(const char* host, int port,
- int test_step) {
+ net::URLRequestStatus::Status FetchDBToVerify(
+ const net::TestServer& test_server,
+ int test_step) {
// TODO(lzheng): Remove chunk_type=add once it is not needed by the server.
- GURL url(base::StringPrintf(
- "http://%s:%d%s?"
- "client=chromium&appver=1.0&pver=2.2&test_step=%d&"
- "chunk_type=add",
- host, port, kDBVerifyPath, test_step));
- return FetchUrl(url);
+ std::string path = base::StringPrintf(
+ "%s?client=chromium&appver=1.0&pver=2.2&test_step=%d&chunk_type=add",
+ kDBVerifyPath, test_step);
+ return FetchUrl(test_server.GetURL(path));
}
// Calls test server to fetch URLs for verification.
- net::URLRequestStatus::Status FetchUrlsToVerify(const char* host, int port,
- int test_step) {
- GURL url(base::StringPrintf(
- "http://%s:%d%s?"
- "client=chromium&appver=1.0&pver=2.2&test_step=%d",
- host, port, kUrlVerifyPath, test_step));
- return FetchUrl(url);
+ net::URLRequestStatus::Status FetchUrlsToVerify(
+ const net::TestServer& test_server,
+ int test_step) {
+ std::string path = base::StringPrintf(
+ "%s?client=chromium&appver=1.0&pver=2.2&test_step=%d",
+ kUrlVerifyPath, test_step);
+ return FetchUrl(test_server.GetURL(path));
}
// Calls test server to check if test data is done. E.g.: if there is a
// bad URL that server expects test to fetch full hash but the test didn't,
// this verification will fail.
- net::URLRequestStatus::Status VerifyTestComplete(const char* host, int port,
- int test_step) {
- GURL url(StringPrintf("http://%s:%d%s?test_step=%d",
- host, port, kTestCompletePath, test_step));
- return FetchUrl(url);
+ net::URLRequestStatus::Status VerifyTestComplete(
+ const net::TestServer& test_server,
+ int test_step) {
+ std::string path = base::StringPrintf(
+ "%s?test_step=%d", kTestCompletePath, test_step);
+ return FetchUrl(test_server.GetURL(path));
}
// Callback for URLFetcher.
@@ -537,8 +441,6 @@ class SafeBrowsingServiceTestHelper
IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest,
DISABLED_SafeBrowsingSystemTest) {
LOG(INFO) << "Start test";
- const char* server_host = SafeBrowsingTestServer::Host();
- int server_port = SafeBrowsingTestServer::Port();
ASSERT_TRUE(InitSafeBrowsingService());
net::URLRequestContextGetter* request_context =
@@ -546,12 +448,6 @@ IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest,
scoped_refptr<SafeBrowsingServiceTestHelper> safe_browsing_helper(
new SafeBrowsingServiceTestHelper(this, request_context));
int last_step = 0;
- FilePath datafile_path = FilePath(kDataFile);
- SafeBrowsingTestServer test_server(datafile_path);
- ASSERT_TRUE(test_server.Start());
-
- // Make sure the server is running.
- safe_browsing_helper->WaitTillServerReady(server_host, server_port);
// Waits and makes sure safebrowsing update is not happening.
// The wait will stop once OnWaitForStatusUpdateDone in
@@ -588,9 +484,7 @@ IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest,
// Fetches URLs to verify and waits till server responses with data.
EXPECT_EQ(net::URLRequestStatus::SUCCESS,
- safe_browsing_helper->FetchUrlsToVerify(server_host,
- server_port,
- step));
+ safe_browsing_helper->FetchUrlsToVerify(test_server(), step));
std::vector<PhishingUrl> phishing_urls;
EXPECT_TRUE(ParsePhishingUrls(safe_browsing_helper->response_data(),
@@ -619,18 +513,13 @@ IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest,
// TODO(lzheng): We should verify the fetched database with local
// database to make sure they match.
EXPECT_EQ(net::URLRequestStatus::SUCCESS,
- safe_browsing_helper->FetchDBToVerify(server_host,
- server_port,
- step));
+ safe_browsing_helper->FetchDBToVerify(test_server(), step));
EXPECT_GT(safe_browsing_helper->response_data().size(), 0U);
last_step = step;
}
// Verifies with server if test is done and waits till server responses.
EXPECT_EQ(net::URLRequestStatus::SUCCESS,
- safe_browsing_helper->VerifyTestComplete(server_host,
- server_port,
- last_step));
+ safe_browsing_helper->VerifyTestComplete(test_server(), last_step));
EXPECT_EQ("yes", safe_browsing_helper->response_data());
- test_server.Stop();
}
diff --git a/chrome/browser/safe_browsing/safe_browsing_testserver.py b/chrome/browser/safe_browsing/safe_browsing_testserver.py
new file mode 100755
index 0000000..674bf5a
--- /dev/null
+++ b/chrome/browser/safe_browsing/safe_browsing_testserver.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Wraps the upstream safebrowsing_test_server.py to run in Chrome tests."""
+
+import os
+import sys
+
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+
+sys.path.append(os.path.join(BASE_DIR, '..', '..', '..', 'net',
+ 'tools', 'testserver'))
+import testserver_base
+
+
+class ServerRunner(testserver_base.TestServerRunner):
+ """TestServerRunner for safebrowsing_test_server.py."""
+
+ def create_server(self, server_data):
+ sys.path.append(os.path.join(BASE_DIR, '..', '..', '..', 'third_party',
+ 'safe_browsing', 'testing'))
+ import safebrowsing_test_server
+ server = safebrowsing_test_server.SetupServer(
+ self.options.data_file, self.options.host, self.options.port,
+ opt_enforce_caching=False, opt_validate_database=True)
+ print 'Safebrowsing HTTP server started on port %d...' % server.server_port
+ server_data['port'] = server.server_port
+
+ return server
+
+ def add_options(self):
+ testserver_base.TestServerRunner.add_options(self)
+ self.option_parser.add_option('--data-file', dest='data_file',
+ help='File containing safebrowsing test '
+ 'data and expectations')
+ # TODO(mattm): we define an unnecessary --data-dir option because
+ # BaseTestServer unconditionally sets --data-dir. This should be removed
+ # when BaseTestServer is refactored to not contain all the net/
+ # test_server.py specific stuff.
+ self.option_parser.add_option('--data-dir', dest='data_dir',
+ help='unused')
+
+
+if __name__ == '__main__':
+ sys.exit(ServerRunner().main())
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index b44e8ab..2227e0c 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -3785,6 +3785,7 @@
],
'sources': [
'app/chrome_dll.rc',
+ 'browser/safe_browsing/local_safebrowsing_test_server.cc',
'browser/safe_browsing/safe_browsing_test.cc',
'test/base/chrome_test_launcher.cc',
],
diff --git a/net/test/base_test_server.cc b/net/test/base_test_server.cc
index d13d57c..9307acf 100644
--- a/net/test/base_test_server.cc
+++ b/net/test/base_test_server.cc
@@ -390,6 +390,12 @@ bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const {
base::Value::CreateIntegerValue(ssl_options_.tls_intolerant));
}
}
+
+ return GenerateAdditionalArguments(arguments);
+}
+
+bool BaseTestServer::GenerateAdditionalArguments(
+ base::DictionaryValue* arguments) const {
return true;
}
diff --git a/net/test/base_test_server.h b/net/test/base_test_server.h
index 9f1290e..d343f18 100644
--- a/net/test/base_test_server.h
+++ b/net/test/base_test_server.h
@@ -214,7 +214,13 @@ class BaseTestServer {
// Generates a DictionaryValue with the arguments for launching the external
// Python test server.
- bool GenerateArguments(base::DictionaryValue* arguments) const;
+ bool GenerateArguments(base::DictionaryValue* arguments) const
+ WARN_UNUSED_RESULT;
+
+ // Subclasses can override this to add arguments that are specific to their
+ // own test servers.
+ virtual bool GenerateAdditionalArguments(
+ base::DictionaryValue* arguments) const WARN_UNUSED_RESULT;
private:
void Init(const std::string& host);
diff --git a/net/test/local_sync_test_server.cc b/net/test/local_sync_test_server.cc
index 8d757f8..432fde1 100644
--- a/net/test/local_sync_test_server.cc
+++ b/net/test/local_sync_test_server.cc
@@ -29,7 +29,8 @@ LocalSyncTestServer::~LocalSyncTestServer() {}
bool LocalSyncTestServer::AddCommandLineArguments(
CommandLine* command_line) const {
- LocalTestServer::AddCommandLineArguments(command_line);
+ if (!LocalTestServer::AddCommandLineArguments(command_line))
+ return false;
if (xmpp_port_ != 0) {
std::string xmpp_port_str = base::IntToString(xmpp_port_);
command_line->AppendArg("--xmpp-port=" + xmpp_port_str);
diff --git a/net/test/local_test_server.cc b/net/test/local_test_server.cc
index 17137a69..53879df 100644
--- a/net/test/local_test_server.cc
+++ b/net/test/local_test_server.cc
@@ -94,13 +94,19 @@ bool LocalTestServer::GetTestServerDirectory(FilePath* directory) {
return true;
}
+bool LocalTestServer::GetTestServerPath(FilePath* testserver_path) const {
+ if (!GetTestServerDirectory(testserver_path))
+ return false;
+ *testserver_path = testserver_path->Append(FILE_PATH_LITERAL(
+ "testserver.py"));
+ return true;
+}
+
bool LocalTestServer::Start() {
// Get path to Python server script.
FilePath testserver_path;
- if (!GetTestServerDirectory(&testserver_path))
+ if (!GetTestServerPath(&testserver_path))
return false;
- testserver_path =
- testserver_path.Append(FILE_PATH_LITERAL("testserver.py"));
if (!SetPythonPath())
return false;
@@ -159,8 +165,12 @@ bool LocalTestServer::Init(const FilePath& document_root) {
return true;
}
+bool LocalTestServer::SetPythonPath() const {
+ return SetPythonPathStatic();
+}
+
// static
-bool LocalTestServer::SetPythonPath() {
+bool LocalTestServer::SetPythonPathStatic() {
FilePath third_party_dir;
if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) {
LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
diff --git a/net/test/local_test_server.h b/net/test/local_test_server.h
index d8d070e..3c6bdde 100644
--- a/net/test/local_test_server.h
+++ b/net/test/local_test_server.h
@@ -43,15 +43,26 @@ class LocalTestServer : public BaseTestServer {
bool Stop();
// Modify PYTHONPATH to contain libraries we need.
- static bool SetPythonPath() WARN_UNUSED_RESULT;
+ virtual bool SetPythonPath() const WARN_UNUSED_RESULT;
+
+ // This is a static version so that RunSyncTest in run_testserver.cc can use
+ // it.
+ // TODO(mattm): We should refactor so this isn't necessary (crbug.com/159731).
+ static bool SetPythonPathStatic() WARN_UNUSED_RESULT;
// Returns true if successfully stored the FilePath for the directory of the
// testserver python script in |*directory|.
static bool GetTestServerDirectory(FilePath* directory) WARN_UNUSED_RESULT;
+ // Returns true if successfully stored the FilePath for the testserver python
+ // script in |*testserver_path|.
+ virtual bool GetTestServerPath(FilePath* testserver_path) const
+ WARN_UNUSED_RESULT;
+
// Adds the command line arguments for the Python test server to
// |command_line|. Returns true on success.
- virtual bool AddCommandLineArguments(CommandLine* command_line) const;
+ virtual bool AddCommandLineArguments(CommandLine* command_line) const
+ WARN_UNUSED_RESULT;
// Returns the actual path of document root for test cases. This function
// should be called by test cases to retrieve the actual document root path.
diff --git a/net/tools/testserver/run_testserver.cc b/net/tools/testserver/run_testserver.cc
index 58abe34..2341512 100644
--- a/net/tools/testserver/run_testserver.cc
+++ b/net/tools/testserver/run_testserver.cc
@@ -28,7 +28,7 @@ static void PrintUsage() {
// Launches the chromiumsync_test script, testing the --sync functionality.
static bool RunSyncTest() {
- if (!net::TestServer::SetPythonPath()) {
+ if (!net::TestServer::SetPythonPathStatic()) {
LOG(ERROR) << "Error trying to set python path. Exiting.";
return false;
}
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index 31fd3c5..ba74b691 100755
--- a/net/tools/testserver/testserver.py
+++ b/net/tools/testserver/testserver.py
@@ -23,30 +23,27 @@ import httplib
import json
import logging
import minica
-import optparse
import os
import random
import re
import select
import socket
import SocketServer
-import struct
import sys
import threading
import time
import urllib
import urlparse
-import warnings
import zlib
import echo_message
from mod_pywebsocket.standalone import WebSocketServer
import pyftpdlib.ftpserver
+import testserver_base
import tlslite
import tlslite.api
-if sys.platform == 'win32':
- import msvcrt
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
SERVER_HTTP = 0
SERVER_FTP = 1
@@ -59,12 +56,14 @@ SERVER_WEBSOCKET = 6
# Default request queue size for WebSocketServer.
_DEFAULT_REQUEST_QUEUE_SIZE = 128
+
# Using debug() seems to cause hangs on XP: see http://crbug.com/64515 .
debug_output = sys.stderr
def debug(string):
debug_output.write(string + "\n")
debug_output.flush()
+
class WebSocketOptions:
"""Holds options for WebSocketServer."""
@@ -88,6 +87,7 @@ class WebSocketOptions:
self.tls_client_ca = None
self.use_basic_auth = False
+
class RecordingSSLSessionCache(object):
"""RecordingSSLSessionCache acts as a TLS session cache and maintains a log of
lookups and inserts in order to test session cache behaviours."""
@@ -142,6 +142,7 @@ class OCSPServer(ClientRestrictingServerMixIn, BaseHTTPServer.HTTPServer):
self.shutdown()
self.thread.join()
+
class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
ClientRestrictingServerMixIn,
StoppableHTTPServer):
@@ -1981,23 +1982,6 @@ class SyncPageHandler(BasePageHandler):
return True
-def MakeDataDir(options):
- if options.data_dir:
- if not os.path.isdir(options.data_dir):
- print 'specified data dir not found: ' + options.data_dir + ' exiting...'
- return None
- my_data_dir = options.data_dir
- else:
- # Create the default path to our data dir, relative to the exe dir.
- my_data_dir = os.path.dirname(sys.argv[0])
- my_data_dir = os.path.join(my_data_dir, "..", "..", "..", "..",
- "test", "data")
-
- #TODO(ibrar): Must use Find* funtion defined in google\tools
- #i.e my_data_dir = FindUpward(my_data_dir, "test", "data")
-
- return my_data_dir
-
class OCSPHandler(BasePageHandler):
def __init__(self, request, client_address, socket_server):
handlers = [self.OCSPResponse]
@@ -2013,6 +1997,7 @@ class OCSPHandler(BasePageHandler):
self.wfile.write(self.ocsp_response)
+
class TCPEchoHandler(SocketServer.BaseRequestHandler):
"""The RequestHandler class for TCP echo server.
@@ -2168,319 +2153,288 @@ class BasicAuthProxyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
self._do_common_method()
-class FileMultiplexer:
- def __init__(self, fd1, fd2) :
- self.__fd1 = fd1
- self.__fd2 = fd2
-
- def __del__(self) :
- self.close()
-
- def write(self, text) :
- self.__fd1.write(text)
- self.__fd2.write(text)
-
- def flush(self) :
- self.__fd1.flush()
- self.__fd2.flush()
-
- def close(self):
- if self.__fd1 != sys.stdout and self.__fd1 != sys.stderr:
- self.__fd1.close()
- if self.__fd2 != sys.stdout and self.__fd2 != sys.stderr:
- self.__fd2.close()
+class ServerRunner(testserver_base.TestServerRunner):
+ """TestServerRunner for the net test servers."""
-
-def main(options, _args):
- logfile = open('testserver.log', 'w')
- sys.stderr = FileMultiplexer(sys.stderr, logfile)
- if options.log_to_console:
- sys.stdout = FileMultiplexer(sys.stdout, logfile)
- else:
- sys.stdout = logfile
-
- port = options.port
- host = options.host
-
- server_data = {}
- server_data['host'] = host
-
- ocsp_server = None
-
- if options.server_type == SERVER_HTTP:
- if options.https:
- pem_cert_and_key = None
- if options.cert_and_key_file:
- if not os.path.isfile(options.cert_and_key_file):
- print ('specified server cert file not found: ' +
- options.cert_and_key_file + ' exiting...')
- return 1
- pem_cert_and_key = file(options.cert_and_key_file, 'r').read()
- else:
- # generate a new certificate and run an OCSP server for it.
- ocsp_server = OCSPServer((host, 0), OCSPHandler)
- print ('OCSP server started on %s:%d...' %
- (host, ocsp_server.server_port))
-
- ocsp_der = None
- ocsp_state = None
-
- if options.ocsp == 'ok':
- ocsp_state = minica.OCSP_STATE_GOOD
- elif options.ocsp == 'revoked':
- ocsp_state = minica.OCSP_STATE_REVOKED
- elif options.ocsp == 'invalid':
- ocsp_state = minica.OCSP_STATE_INVALID
- elif options.ocsp == 'unauthorized':
- ocsp_state = minica.OCSP_STATE_UNAUTHORIZED
- elif options.ocsp == 'unknown':
- ocsp_state = minica.OCSP_STATE_UNKNOWN
- else:
- print 'unknown OCSP status: ' + options.ocsp_status
- return 1
-
- (pem_cert_and_key, ocsp_der) = minica.GenerateCertKeyAndOCSP(
- subject = "127.0.0.1",
- ocsp_url = ("http://%s:%d/ocsp" % (host, ocsp_server.server_port)),
- ocsp_state = ocsp_state)
-
- ocsp_server.ocsp_response = ocsp_der
-
- for ca_cert in options.ssl_client_ca:
- if not os.path.isfile(ca_cert):
- print ('specified trusted client CA file not found: ' + ca_cert +
- ' exiting...')
- return 1
- server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key,
- options.ssl_client_auth, options.ssl_client_ca,
- options.ssl_bulk_cipher, options.record_resume,
- options.tls_intolerant)
- print 'HTTPS server started on %s:%d...' % (host, server.server_port)
+ def __init__(self):
+ super(ServerRunner, self).__init__()
+ self.__ocsp_server = None
+
+ def __make_data_dir(self):
+ if self.options.data_dir:
+ if not os.path.isdir(self.options.data_dir):
+ raise testserver_base.OptionError('specified data dir not found: ' +
+ self.options.data_dir + ' exiting...')
+ my_data_dir = self.options.data_dir
else:
- server = HTTPServer((host, port), TestPageHandler)
- print 'HTTP server started on %s:%d...' % (host, server.server_port)
-
- server.data_dir = MakeDataDir(options)
- server.file_root_url = options.file_root_url
- server_data['port'] = server.server_port
- server._device_management_handler = None
- server.policy_keys = options.policy_keys
- server.policy_user = options.policy_user
- server.gdata_auth_token = options.auth_token
- elif options.server_type == SERVER_WEBSOCKET:
- # Launch pywebsocket via WebSocketServer.
- logger = logging.getLogger()
- logger.addHandler(logging.StreamHandler())
- # TODO(toyoshim): Remove following os.chdir. Currently this operation
- # is required to work correctly. It should be fixed from pywebsocket side.
- os.chdir(MakeDataDir(options))
- websocket_options = WebSocketOptions(host, port, '.')
- if options.cert_and_key_file:
- websocket_options.use_tls = True
- websocket_options.private_key = options.cert_and_key_file
- websocket_options.certificate = options.cert_and_key_file
- if options.ssl_client_auth:
- websocket_options.tls_client_auth = True
- if len(options.ssl_client_ca) != 1:
- print 'one trusted client CA file should be specified'
- return 1
- if not os.path.isfile(options.ssl_client_ca[0]):
- print ('specified trusted client CA file not found: ' +
- options.ssl_client_ca[0] + ' exiting...')
- return 1
- websocket_options.tls_client_ca = options.ssl_client_ca[0]
- server = WebSocketServer(websocket_options)
- print 'WebSocket server started on %s:%d...' % (host, server.server_port)
- server_data['port'] = server.server_port
- elif options.server_type == SERVER_SYNC:
- xmpp_port = options.xmpp_port
- server = SyncHTTPServer((host, port), xmpp_port, SyncPageHandler)
- print 'Sync HTTP server started on port %d...' % server.server_port
- print 'Sync XMPP server started on port %d...' % server.xmpp_port
- server_data['port'] = server.server_port
- server_data['xmpp_port'] = server.xmpp_port
- elif options.server_type == SERVER_TCP_ECHO:
- # Used for generating the key (randomly) that encodes the "echo request"
- # message.
- random.seed()
- server = TCPEchoServer((host, port), TCPEchoHandler)
- print 'Echo TCP server started on port %d...' % server.server_port
- server_data['port'] = server.server_port
- elif options.server_type == SERVER_UDP_ECHO:
- # Used for generating the key (randomly) that encodes the "echo request"
- # message.
- random.seed()
- server = UDPEchoServer((host, port), UDPEchoHandler)
- print 'Echo UDP server started on port %d...' % server.server_port
- server_data['port'] = server.server_port
- elif options.server_type == SERVER_BASIC_AUTH_PROXY:
- server = HTTPServer((host, port), BasicAuthProxyRequestHandler)
- print 'BasicAuthProxy server started on port %d...' % server.server_port
- server_data['port'] = server.server_port
- # means FTP Server
- else:
- my_data_dir = MakeDataDir(options)
-
- # Instantiate a dummy authorizer for managing 'virtual' users
- authorizer = pyftpdlib.ftpserver.DummyAuthorizer()
-
- # Define a new user having full r/w permissions and a read-only
- # anonymous user
- authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw')
-
- authorizer.add_anonymous(my_data_dir)
-
- # Instantiate FTP handler class
- ftp_handler = pyftpdlib.ftpserver.FTPHandler
- ftp_handler.authorizer = authorizer
-
- # Define a customized banner (string returned when client connects)
- ftp_handler.banner = ("pyftpdlib %s based ftpd ready." %
- pyftpdlib.ftpserver.__ver__)
-
- # Instantiate FTP server class and listen to address:port
- server = pyftpdlib.ftpserver.FTPServer((host, port), ftp_handler)
- server_data['port'] = server.socket.getsockname()[1]
- print 'FTP server started on port %d...' % server_data['port']
-
- # Notify the parent that we've started. (BaseServer subclasses
- # bind their sockets on construction.)
- if options.startup_pipe is not None:
- server_data_json = json.dumps(server_data)
- server_data_len = len(server_data_json)
- print 'sending server_data: %s (%d bytes)' % (
- server_data_json, server_data_len)
- if sys.platform == 'win32':
- fd = msvcrt.open_osfhandle(options.startup_pipe, 0)
+ # Create the default path to our data dir, relative to the exe dir.
+ my_data_dir = os.path.join(BASE_DIR, "..", "..", "..", "..",
+ "test", "data")
+
+ #TODO(ibrar): Must use Find* funtion defined in google\tools
+ #i.e my_data_dir = FindUpward(my_data_dir, "test", "data")
+
+ return my_data_dir
+
+ def create_server(self, server_data):
+ port = self.options.port
+ host = self.options.host
+
+ if self.options.server_type == SERVER_HTTP:
+ if self.options.https:
+ pem_cert_and_key = None
+ if self.options.cert_and_key_file:
+ if not os.path.isfile(self.options.cert_and_key_file):
+ raise testserver_base.OptionError(
+ 'specified server cert file not found: ' +
+ self.options.cert_and_key_file + ' exiting...')
+ pem_cert_and_key = file(self.options.cert_and_key_file, 'r').read()
+ else:
+ # generate a new certificate and run an OCSP server for it.
+ self.__ocsp_server = OCSPServer((host, 0), OCSPHandler)
+ print ('OCSP server started on %s:%d...' %
+ (host, self.__ocsp_server.server_port))
+
+ ocsp_der = None
+ ocsp_state = None
+
+ if self.options.ocsp == 'ok':
+ ocsp_state = minica.OCSP_STATE_GOOD
+ elif self.options.ocsp == 'revoked':
+ ocsp_state = minica.OCSP_STATE_REVOKED
+ elif self.options.ocsp == 'invalid':
+ ocsp_state = minica.OCSP_STATE_INVALID
+ elif self.options.ocsp == 'unauthorized':
+ ocsp_state = minica.OCSP_STATE_UNAUTHORIZED
+ elif self.options.ocsp == 'unknown':
+ ocsp_state = minica.OCSP_STATE_UNKNOWN
+ else:
+ raise testserver_base.OptionError('unknown OCSP status: ' +
+ self.options.ocsp_status)
+
+ (pem_cert_and_key, ocsp_der) = minica.GenerateCertKeyAndOCSP(
+ subject = "127.0.0.1",
+ ocsp_url = ("http://%s:%d/ocsp" %
+ (host, self.__ocsp_server.server_port)),
+ ocsp_state = ocsp_state)
+
+ self.__ocsp_server.ocsp_response = ocsp_der
+
+ for ca_cert in self.options.ssl_client_ca:
+ if not os.path.isfile(ca_cert):
+ raise testserver_base.OptionError(
+ 'specified trusted client CA file not found: ' + ca_cert +
+ ' exiting...')
+ server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key,
+ self.options.ssl_client_auth,
+ self.options.ssl_client_ca,
+ self.options.ssl_bulk_cipher,
+ self.options.record_resume,
+ self.options.tls_intolerant)
+ print 'HTTPS server started on %s:%d...' % (host, server.server_port)
+ else:
+ server = HTTPServer((host, port), TestPageHandler)
+ print 'HTTP server started on %s:%d...' % (host, server.server_port)
+
+ server.data_dir = self.__make_data_dir()
+ server.file_root_url = self.options.file_root_url
+ server_data['port'] = server.server_port
+ server._device_management_handler = None
+ server.policy_keys = self.options.policy_keys
+ server.policy_user = self.options.policy_user
+ server.gdata_auth_token = self.options.auth_token
+ elif self.options.server_type == SERVER_WEBSOCKET:
+ # Launch pywebsocket via WebSocketServer.
+ logger = logging.getLogger()
+ logger.addHandler(logging.StreamHandler())
+ # TODO(toyoshim): Remove following os.chdir. Currently this operation
+ # is required to work correctly. It should be fixed from pywebsocket side.
+ os.chdir(self.__make_data_dir())
+ websocket_options = WebSocketOptions(host, port, '.')
+ if self.options.cert_and_key_file:
+ websocket_options.use_tls = True
+ websocket_options.private_key = self.options.cert_and_key_file
+ websocket_options.certificate = self.options.cert_and_key_file
+ if self.options.ssl_client_auth:
+ websocket_options.tls_client_auth = True
+ if len(self.options.ssl_client_ca) != 1:
+ raise testserver_base.OptionError(
+ 'one trusted client CA file should be specified')
+ if not os.path.isfile(self.options.ssl_client_ca[0]):
+ raise testserver_base.OptionError(
+ 'specified trusted client CA file not found: ' +
+ self.options.ssl_client_ca[0] + ' exiting...')
+ websocket_options.tls_client_ca = self.options.ssl_client_ca[0]
+ server = WebSocketServer(websocket_options)
+ print 'WebSocket server started on %s:%d...' % (host, server.server_port)
+ server_data['port'] = server.server_port
+ elif self.options.server_type == SERVER_SYNC:
+ xmpp_port = self.options.xmpp_port
+ server = SyncHTTPServer((host, port), xmpp_port, SyncPageHandler)
+ print 'Sync HTTP server started on port %d...' % server.server_port
+ print 'Sync XMPP server started on port %d...' % server.xmpp_port
+ server_data['port'] = server.server_port
+ server_data['xmpp_port'] = server.xmpp_port
+ elif self.options.server_type == SERVER_TCP_ECHO:
+ # Used for generating the key (randomly) that encodes the "echo request"
+ # message.
+ random.seed()
+ server = TCPEchoServer((host, port), TCPEchoHandler)
+ print 'Echo TCP server started on port %d...' % server.server_port
+ server_data['port'] = server.server_port
+ elif self.options.server_type == SERVER_UDP_ECHO:
+ # Used for generating the key (randomly) that encodes the "echo request"
+ # message.
+ random.seed()
+ server = UDPEchoServer((host, port), UDPEchoHandler)
+ print 'Echo UDP server started on port %d...' % server.server_port
+ server_data['port'] = server.server_port
+ elif self.options.server_type == SERVER_BASIC_AUTH_PROXY:
+ server = HTTPServer((host, port), BasicAuthProxyRequestHandler)
+ print 'BasicAuthProxy server started on port %d...' % server.server_port
+ server_data['port'] = server.server_port
+ elif self.options.server_type == SERVER_FTP:
+ my_data_dir = self.__make_data_dir()
+
+ # Instantiate a dummy authorizer for managing 'virtual' users
+ authorizer = pyftpdlib.ftpserver.DummyAuthorizer()
+
+ # Define a new user having full r/w permissions and a read-only
+ # anonymous user
+ authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw')
+
+ authorizer.add_anonymous(my_data_dir)
+
+ # Instantiate FTP handler class
+ ftp_handler = pyftpdlib.ftpserver.FTPHandler
+ ftp_handler.authorizer = authorizer
+
+ # Define a customized banner (string returned when client connects)
+ ftp_handler.banner = ("pyftpdlib %s based ftpd ready." %
+ pyftpdlib.ftpserver.__ver__)
+
+ # Instantiate FTP server class and listen to address:port
+ server = pyftpdlib.ftpserver.FTPServer((host, port), ftp_handler)
+ server_data['port'] = server.socket.getsockname()[1]
+ print 'FTP server started on port %d...' % server_data['port']
else:
- fd = options.startup_pipe
- startup_pipe = os.fdopen(fd, "w")
- # First write the data length as an unsigned 4-byte value. This
- # is _not_ using network byte ordering since the other end of the
- # pipe is on the same machine.
- startup_pipe.write(struct.pack('=L', server_data_len))
- startup_pipe.write(server_data_json)
- startup_pipe.close()
-
- if ocsp_server is not None:
- ocsp_server.serve_forever_on_thread()
-
- try:
- server.serve_forever()
- except KeyboardInterrupt:
- print 'shutting down server'
- if ocsp_server is not None:
- ocsp_server.stop_serving()
- server.stop = True
-
- return 0
+ raise testserver_base.OptionError('unknown server type' +
+ self.options.server_type)
+
+ return server
+
+ def run_server(self):
+ if self.__ocsp_server:
+ self.__ocsp_server.serve_forever_on_thread()
+
+ testserver_base.TestServerRunner.run_server(self)
+
+ if self.__ocsp_server:
+ self.__ocsp_server.stop_serving()
+
+ def add_options(self):
+ testserver_base.TestServerRunner.add_options(self)
+ self.option_parser.add_option('-f', '--ftp', action='store_const',
+ const=SERVER_FTP, default=SERVER_HTTP,
+ dest='server_type',
+ help='start up an FTP server.')
+ self.option_parser.add_option('--sync', action='store_const',
+ const=SERVER_SYNC, default=SERVER_HTTP,
+ dest='server_type',
+ help='start up a sync server.')
+ self.option_parser.add_option('--tcp-echo', action='store_const',
+ const=SERVER_TCP_ECHO, default=SERVER_HTTP,
+ dest='server_type',
+ help='start up a tcp echo server.')
+ self.option_parser.add_option('--udp-echo', action='store_const',
+ const=SERVER_UDP_ECHO, default=SERVER_HTTP,
+ dest='server_type',
+ help='start up a udp echo server.')
+ self.option_parser.add_option('--basic-auth-proxy', action='store_const',
+ const=SERVER_BASIC_AUTH_PROXY,
+ default=SERVER_HTTP, dest='server_type',
+ help='start up a proxy server which requires '
+ 'basic authentication.')
+ self.option_parser.add_option('--websocket', action='store_const',
+ const=SERVER_WEBSOCKET, default=SERVER_HTTP,
+ dest='server_type',
+ help='start up a WebSocket server.')
+ self.option_parser.add_option('--xmpp-port', default='0', type='int',
+ help='Port used by the XMPP server. If '
+ 'unspecified, the XMPP server will listen on '
+ 'an ephemeral port.')
+ self.option_parser.add_option('--data-dir', dest='data_dir',
+ help='Directory from which to read the '
+ 'files.')
+ self.option_parser.add_option('--https', action='store_true',
+ dest='https', help='Specify that https '
+ 'should be used.')
+ self.option_parser.add_option('--cert-and-key-file',
+ dest='cert_and_key_file', help='specify the '
+ 'path to the file containing the certificate '
+ 'and private key for the server in PEM '
+ 'format')
+ self.option_parser.add_option('--ocsp', dest='ocsp', default='ok',
+ help='The type of OCSP response generated '
+ 'for the automatically generated '
+ 'certificate. One of [ok,revoked,invalid]')
+ self.option_parser.add_option('--tls-intolerant', dest='tls_intolerant',
+ default='0', type='int',
+ help='If nonzero, certain TLS connections '
+ 'will be aborted in order to test version '
+ 'fallback. 1 means all TLS versions will be '
+ 'aborted. 2 means TLS 1.1 or higher will be '
+ 'aborted. 3 means TLS 1.2 or higher will be '
+ 'aborted.')
+ self.option_parser.add_option('--https-record-resume',
+ dest='record_resume', const=True,
+ default=False, action='store_const',
+ help='Record resumption cache events rather '
+ 'than resuming as normal. Allows the use of '
+ 'the /ssl-session-cache request')
+ self.option_parser.add_option('--ssl-client-auth', action='store_true',
+ help='Require SSL client auth on every '
+ 'connection.')
+ self.option_parser.add_option('--ssl-client-ca', action='append',
+ default=[], help='Specify that the client '
+ 'certificate request should include the CA '
+ 'named in the subject of the DER-encoded '
+ 'certificate contained in the specified '
+ 'file. This option may appear multiple '
+ 'times, indicating multiple CA names should '
+ 'be sent in the request.')
+ self.option_parser.add_option('--ssl-bulk-cipher', action='append',
+ help='Specify the bulk encryption '
+ 'algorithm(s) that will be accepted by the '
+ 'SSL server. Valid values are "aes256", '
+ '"aes128", "3des", "rc4". If omitted, all '
+ 'algorithms will be used. This option may '
+ 'appear multiple times, indicating '
+ 'multiple algorithms should be enabled.');
+ self.option_parser.add_option('--file-root-url', default='/files/',
+ help='Specify a root URL for files served.')
+ self.option_parser.add_option('--policy-key', action='append',
+ dest='policy_keys',
+ help='Specify a path to a PEM-encoded '
+ 'private key to use for policy signing. May '
+ 'be specified multiple times in order to '
+ 'load multipe keys into the server. If the '
+ 'server has multiple keys, it will rotate '
+ 'through them in at each request a '
+ 'round-robin fashion. The server will '
+ 'generate a random key if none is specified '
+ 'on the command line.')
+ self.option_parser.add_option('--policy-user',
+ default='user@example.com',
+ dest='policy_user',
+ help='Specify the user name the server '
+ 'should report back to the client as the '
+ 'user owning the token used for making the '
+ 'policy request.')
+ self.option_parser.add_option('--auth-token', dest='auth_token',
+ help='Specify the auth token which should be '
+ 'used in the authorization header for GData.')
+
if __name__ == '__main__':
- option_parser = optparse.OptionParser()
- option_parser.add_option("-f", '--ftp', action='store_const',
- const=SERVER_FTP, default=SERVER_HTTP,
- dest='server_type',
- help='start up an FTP server.')
- option_parser.add_option('', '--sync', action='store_const',
- const=SERVER_SYNC, default=SERVER_HTTP,
- dest='server_type',
- help='start up a sync server.')
- option_parser.add_option('', '--tcp-echo', action='store_const',
- const=SERVER_TCP_ECHO, default=SERVER_HTTP,
- dest='server_type',
- help='start up a tcp echo server.')
- option_parser.add_option('', '--udp-echo', action='store_const',
- const=SERVER_UDP_ECHO, default=SERVER_HTTP,
- dest='server_type',
- help='start up a udp echo server.')
- option_parser.add_option('', '--basic-auth-proxy', action='store_const',
- const=SERVER_BASIC_AUTH_PROXY, default=SERVER_HTTP,
- dest='server_type',
- help='start up a proxy server which requires basic '
- 'authentication.')
- option_parser.add_option('', '--websocket', action='store_const',
- const=SERVER_WEBSOCKET, default=SERVER_HTTP,
- dest='server_type',
- help='start up a WebSocket server.')
- option_parser.add_option('', '--log-to-console', action='store_const',
- const=True, default=False,
- dest='log_to_console',
- help='Enables or disables sys.stdout logging to '
- 'the console.')
- option_parser.add_option('', '--port', default='0', type='int',
- help='Port used by the server. If unspecified, the '
- 'server will listen on an ephemeral port.')
- option_parser.add_option('', '--xmpp-port', default='0', type='int',
- help='Port used by the XMPP server. If unspecified, '
- 'the XMPP server will listen on an ephemeral port.')
- option_parser.add_option('', '--data-dir', dest='data_dir',
- help='Directory from which to read the files.')
- option_parser.add_option('', '--https', action='store_true', dest='https',
- help='Specify that https should be used.')
- option_parser.add_option('', '--cert-and-key-file', dest='cert_and_key_file',
- help='specify the path to the file containing the '
- 'certificate and private key for the server in PEM '
- 'format')
- option_parser.add_option('', '--ocsp', dest='ocsp', default='ok',
- help='The type of OCSP response generated for the '
- 'automatically generated certificate. One of '
- '[ok,revoked,invalid]')
- option_parser.add_option('', '--tls-intolerant', dest='tls_intolerant',
- default='0', type='int',
- help='If nonzero, certain TLS connections will be'
- ' aborted in order to test version fallback. 1'
- ' means all TLS versions will be aborted. 2 means'
- ' TLS 1.1 or higher will be aborted. 3 means TLS'
- ' 1.2 or higher will be aborted.')
- option_parser.add_option('', '--https-record-resume', dest='record_resume',
- const=True, default=False, action='store_const',
- help='Record resumption cache events rather than'
- ' resuming as normal. Allows the use of the'
- ' /ssl-session-cache request')
- option_parser.add_option('', '--ssl-client-auth', action='store_true',
- help='Require SSL client auth on every connection.')
- option_parser.add_option('', '--ssl-client-ca', action='append', default=[],
- help='Specify that the client certificate request '
- 'should include the CA named in the subject of '
- 'the DER-encoded certificate contained in the '
- 'specified file. This option may appear multiple '
- 'times, indicating multiple CA names should be '
- 'sent in the request.')
- option_parser.add_option('', '--ssl-bulk-cipher', action='append',
- help='Specify the bulk encryption algorithm(s)'
- 'that will be accepted by the SSL server. Valid '
- 'values are "aes256", "aes128", "3des", "rc4". If '
- 'omitted, all algorithms will be used. This '
- 'option may appear multiple times, indicating '
- 'multiple algorithms should be enabled.')
- option_parser.add_option('', '--file-root-url', default='/files/',
- help='Specify a root URL for files served.')
- option_parser.add_option('', '--startup-pipe', type='int',
- dest='startup_pipe',
- help='File handle of pipe to parent process')
- option_parser.add_option('', '--policy-key', action='append',
- dest='policy_keys',
- help='Specify a path to a PEM-encoded private key '
- 'to use for policy signing. May be specified '
- 'multiple times in order to load multipe keys into '
- 'the server. If ther server has multiple keys, it '
- 'will rotate through them in at each request a '
- 'round-robin fashion. The server will generate a '
- 'random key if none is specified on the command '
- 'line.')
- option_parser.add_option('', '--policy-user', default='user@example.com',
- dest='policy_user',
- help='Specify the user name the server should '
- 'report back to the client as the user owning the '
- 'token used for making the policy request.')
- option_parser.add_option('', '--host', default='127.0.0.1',
- dest='host',
- help='Hostname or IP upon which the server will '
- 'listen. Client connections will also only be '
- 'allowed from this address.')
- option_parser.add_option('', '--auth-token', dest='auth_token',
- help='Specify the auth token which should be used'
- 'in the authorization header for GData.')
- main_options, main_args = option_parser.parse_args()
-
- sys.exit(main(main_options, main_args))
+ sys.exit(ServerRunner().main())
diff --git a/net/tools/testserver/testserver_base.py b/net/tools/testserver/testserver_base.py
new file mode 100644
index 0000000..076943e
--- /dev/null
+++ b/net/tools/testserver/testserver_base.py
@@ -0,0 +1,126 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import optparse
+import os
+import struct
+import sys
+import warnings
+
+# Ignore deprecation warnings, they make our output more cluttered.
+warnings.filterwarnings("ignore", category=DeprecationWarning)
+
+if sys.platform == 'win32':
+ import msvcrt
+
+
+class Error(Exception):
+ """Error class for this module."""
+
+
+class OptionError(Error):
+ """Error for bad command line options."""
+
+
+class FileMultiplexer(object):
+ def __init__(self, fd1, fd2) :
+ self.__fd1 = fd1
+ self.__fd2 = fd2
+
+ def __del__(self) :
+ if self.__fd1 != sys.stdout and self.__fd1 != sys.stderr:
+ self.__fd1.close()
+ if self.__fd2 != sys.stdout and self.__fd2 != sys.stderr:
+ self.__fd2.close()
+
+ def write(self, text) :
+ self.__fd1.write(text)
+ self.__fd2.write(text)
+
+ def flush(self) :
+ self.__fd1.flush()
+ self.__fd2.flush()
+
+
+class TestServerRunner(object):
+ """Runs a test server and communicates with the controlling C++ test code.
+
+ Subclasses should override the create_server method to create their server
+ object, and the add_options method to add their own options.
+ """
+
+ def __init__(self):
+ self.option_parser = optparse.OptionParser()
+ self.add_options()
+
+ def main(self):
+ self.options, self.args = self.option_parser.parse_args()
+
+ logfile = open('testserver.log', 'w')
+ sys.stderr = FileMultiplexer(sys.stderr, logfile)
+ if self.options.log_to_console:
+ sys.stdout = FileMultiplexer(sys.stdout, logfile)
+ else:
+ sys.stdout = logfile
+
+ server_data = {
+ 'host': self.options.host,
+ }
+ self.server = self.create_server(server_data)
+ self._notify_startup_complete(server_data)
+ self.run_server()
+
+ def create_server(self, server_data):
+ """Creates a server object and returns it.
+
+ Must populate server_data['port'], and can set additional server_data
+ elements if desired."""
+ raise NotImplementedError()
+
+ def run_server(self):
+ try:
+ self.server.serve_forever()
+ except KeyboardInterrupt:
+ print 'shutting down server'
+ self.server.stop = True
+
+ def add_options(self):
+ self.option_parser.add_option('--startup-pipe', type='int',
+ dest='startup_pipe',
+ help='File handle of pipe to parent process')
+ self.option_parser.add_option('--log-to-console', action='store_const',
+ const=True, default=False,
+ dest='log_to_console',
+ help='Enables or disables sys.stdout logging '
+ 'to the console.')
+ self.option_parser.add_option('--port', default=0, type='int',
+ help='Port used by the server. If '
+ 'unspecified, the server will listen on an '
+ 'ephemeral port.')
+ self.option_parser.add_option('--host', default='127.0.0.1',
+ dest='host',
+ help='Hostname or IP upon which the server '
+ 'will listen. Client connections will also '
+ 'only be allowed from this address.')
+
+ def _notify_startup_complete(self, server_data):
+ # Notify the parent that we've started. (BaseServer subclasses
+ # bind their sockets on construction.)
+ if self.options.startup_pipe is not None:
+ server_data_json = json.dumps(server_data)
+ server_data_len = len(server_data_json)
+ print 'sending server_data: %s (%d bytes)' % (
+ server_data_json, server_data_len)
+ if sys.platform == 'win32':
+ fd = msvcrt.open_osfhandle(self.options.startup_pipe, 0)
+ else:
+ fd = self.options.startup_pipe
+ startup_pipe = os.fdopen(fd, "w")
+ # First write the data length as an unsigned 4-byte value. This
+ # is _not_ using network byte ordering since the other end of the
+ # pipe is on the same machine.
+ startup_pipe.write(struct.pack('=L', server_data_len))
+ startup_pipe.write(server_data_json)
+ startup_pipe.close()