Commit c73b6a46 authored by xunjieli's avatar xunjieli Committed by Commit bot

[Cronet] Add support to run quic_simple_server

This CL added a wrapper on top of quic_simple_server so that
Cronet tests can spin up a local quic server on the device.

BUG=411010

Review URL: https://codereview.chromium.org/1039693003

Cr-Commit-Position: refs/heads/master@{#324001}
parent 6c9d8203
...@@ -261,6 +261,7 @@ ...@@ -261,6 +261,7 @@
'cronet/android/test/src/org/chromium/net/MockUrlRequestJobFactory.java', 'cronet/android/test/src/org/chromium/net/MockUrlRequestJobFactory.java',
'cronet/android/test/src/org/chromium/net/NativeTestServer.java', 'cronet/android/test/src/org/chromium/net/NativeTestServer.java',
'cronet/android/test/src/org/chromium/net/NetworkChangeNotifierUtil.java', 'cronet/android/test/src/org/chromium/net/NetworkChangeNotifierUtil.java',
'cronet/android/test/src/org/chromium/net/QuicTestServer.java',
'cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java', 'cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java',
], ],
'variables': { 'variables': {
...@@ -277,6 +278,8 @@ ...@@ -277,6 +278,8 @@
'cronet/android/test/mock_url_request_job_factory.h', 'cronet/android/test/mock_url_request_job_factory.h',
'cronet/android/test/native_test_server.cc', 'cronet/android/test/native_test_server.cc',
'cronet/android/test/native_test_server.h', 'cronet/android/test/native_test_server.h',
'cronet/android/test/quic_test_server.cc',
'cronet/android/test/quic_test_server.h',
'cronet/android/test/test_upload_data_stream_handler.cc', 'cronet/android/test/test_upload_data_stream_handler.cc',
'cronet/android/test/test_upload_data_stream_handler.h', 'cronet/android/test/test_upload_data_stream_handler.h',
'cronet/android/test/network_change_notifier_util.cc', 'cronet/android/test/network_change_notifier_util.cc',
...@@ -287,6 +290,7 @@ ...@@ -287,6 +290,7 @@
'cronet_tests_jni_headers', 'cronet_tests_jni_headers',
'../base/base.gyp:base', '../base/base.gyp:base',
'../net/net.gyp:net', '../net/net.gyp:net',
'../net/net.gyp:net_quic_proto',
'../net/net.gyp:net_test_support', '../net/net.gyp:net_test_support',
'../net/net.gyp:simple_quic_tools', '../net/net.gyp:simple_quic_tools',
'../url/url.gyp:url_lib', '../url/url.gyp:url_lib',
......
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: text/plain
X-Original-Url: http://127.0.0.1:6121/simple.txt
This is a simple text file served by QUIC.
...@@ -11,13 +11,15 @@ ...@@ -11,13 +11,15 @@
#include "mock_url_request_job_factory.h" #include "mock_url_request_job_factory.h"
#include "native_test_server.h" #include "native_test_server.h"
#include "network_change_notifier_util.h" #include "network_change_notifier_util.h"
#include "quic_test_server.h"
#include "test_upload_data_stream_handler.h" #include "test_upload_data_stream_handler.h"
namespace { namespace {
const base::android::RegistrationMethod kCronetTestsRegisteredMethods[] = { const base::android::RegistrationMethod kCronetTestsRegisteredMethods[] = {
{"MockUrlRequestJobFactory", cronet::RegisterMockUrlRequestJobFactory}, {"MockUrlRequestJobFactory", cronet::RegisterMockUrlRequestJobFactory},
{"RegisterNativeTestServer", cronet::RegisterNativeTestServer}, {"NativeTestServer", cronet::RegisterNativeTestServer},
{"QuicTestServer", cronet::RegisterQuicTestServer},
{"NetworkChangeNotifierUtil", cronet::RegisterNetworkChangeNotifierUtil}, {"NetworkChangeNotifierUtil", cronet::RegisterNetworkChangeNotifierUtil},
{"TestUploadDataStreamHandlerRegisterJni", {"TestUploadDataStreamHandlerRegisterJni",
cronet::TestUploadDataStreamHandlerRegisterJni}, cronet::TestUploadDataStreamHandlerRegisterJni},
......
...@@ -132,50 +132,6 @@ public class CronetUrlTest extends CronetTestBase { ...@@ -132,50 +132,6 @@ public class CronetUrlTest extends CronetTestBase {
} }
} }
@SmallTest
@Feature({"Cronet"})
public void disabled_testQuicLoadUrl() throws Exception {
HttpUrlRequestFactoryConfig config = new HttpUrlRequestFactoryConfig();
// TODO(mef): Test Quic end-to-end using local QUIC server.
String quicURL = "https://www.google.com:443";
String quicNegotiatedProtocol = "quic/1+spdy/3";
config.enableQUIC(true);
config.addQuicHint("www.google.com", 443, 443);
config.setExperimentalQuicConnectionOptions("PACE,IW10,FOO,DEADBEEF");
String[] commandLineArgs = {
CronetTestActivity.CONFIG_KEY, config.toString() };
CronetTestActivity activity =
launchCronetTestAppWithUrlAndCommandLineArgs(null,
commandLineArgs);
activity.startNetLog();
HashMap<String, String> headers = new HashMap<String, String>();
TestHttpUrlRequestListener listener = new TestHttpUrlRequestListener();
// Try several times as first request may not use QUIC.
// TODO(mef): Remove loop after adding http server properties manager.
for (int i = 0; i < 10; ++i) {
HttpUrlRequest request =
activity.mRequestFactory.createRequest(
quicURL,
HttpUrlRequest.REQUEST_PRIORITY_MEDIUM,
headers,
listener);
request.start();
listener.blockForComplete();
assertEquals(200, listener.mHttpStatusCode);
if (listener.mNegotiatedProtocol.equals(quicNegotiatedProtocol)) {
break;
}
Thread.sleep(1000);
listener.resetComplete();
}
assertEquals(quicNegotiatedProtocol, listener.mNegotiatedProtocol);
activity.stopNetLog();
}
@SmallTest @SmallTest
@Feature({"Cronet"}) @Feature({"Cronet"})
public void testLegacyLoadUrl() throws Exception { public void testLegacyLoadUrl() throws Exception {
......
// Copyright 2015 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.
package org.chromium.net;
import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.base.test.util.Feature;
import java.util.HashMap;
/**
* Tests making requests using QUIC.
*/
public class QuicTest extends CronetTestBase {
@Override
protected void setUp() throws Exception {
super.setUp();
// Loads library first since native functions are used to retrieve
// QuicTestServer info before config is constructed.
System.loadLibrary("cronet_tests");
}
@SmallTest
@Feature({"Cronet"})
public void testQuicLoadUrl() throws Exception {
HttpUrlRequestFactoryConfig config = new HttpUrlRequestFactoryConfig();
String quicURL = QuicTestServer.getServerURL() + "/simple.txt";
config.enableQUIC(true);
config.setLibraryName("cronet_tests");
config.addQuicHint(QuicTestServer.getServerHost(),
QuicTestServer.getServerPort(), QuicTestServer.getServerPort());
config.setExperimentalQuicConnectionOptions("PACE,IW10,FOO,DEADBEEF");
String[] commandLineArgs = {
CronetTestActivity.CONFIG_KEY, config.toString() };
CronetTestActivity activity =
launchCronetTestAppWithUrlAndCommandLineArgs(null,
commandLineArgs);
QuicTestServer.startQuicTestServer(activity.getApplicationContext());
activity.startNetLog();
HashMap<String, String> headers = new HashMap<String, String>();
TestHttpUrlRequestListener listener = new TestHttpUrlRequestListener();
HttpUrlRequest request =
activity.mRequestFactory.createRequest(
quicURL,
HttpUrlRequest.REQUEST_PRIORITY_MEDIUM,
headers,
listener);
request.start();
listener.blockForComplete();
assertEquals(200, listener.mHttpStatusCode);
assertEquals(
"This is a simple text file served by QUIC.\n", listener.mResponseAsString);
assertEquals("quic/1+spdy/3", listener.mNegotiatedProtocol);
activity.stopNetLog();
QuicTestServer.shutdownQuicTestServer();
}
}
// Copyright 2015 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 "quic_test_server.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/threading/thread.h"
#include "jni/QuicTestServer_jni.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_util.h"
#include "net/tools/quic/quic_in_memory_cache.h"
#include "net/tools/quic/quic_simple_server.h"
namespace cronet {
namespace {
static const char kServerHost[] = "127.0.0.1";
static const int kServerPort = 6121;
base::Thread* g_quic_server_thread = nullptr;
net::tools::QuicSimpleServer* g_quic_server = nullptr;
void ServeFilesFromDirectory(const base::FilePath& directory) {
DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread());
DCHECK(!g_quic_server);
base::FilePath file_dir = directory.Append("quic_data");
net::tools::QuicInMemoryCache::GetInstance()->InitializeFromDirectory(
file_dir.value());
net::IPAddressNumber ip;
DCHECK(net::ParseIPLiteralToNumber(kServerHost, &ip));
net::QuicConfig config;
g_quic_server =
new net::tools::QuicSimpleServer(config, net::QuicSupportedVersions());
DCHECK(g_quic_server->Listen(net::IPEndPoint(ip, kServerPort)) >= 0) <<
"Quic server fails to start";
}
void ShutdownOnServerThread() {
DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread());
g_quic_server->Shutdown();
delete g_quic_server;
}
} // namespace
// Quic server is currently hardcoded to run on port 6121 of the localhost on
// the device.
void StartQuicTestServer(JNIEnv* env,
jclass jcaller,
jstring jtest_files_root) {
DCHECK(!g_quic_server_thread);
g_quic_server_thread = new base::Thread("quic server thread");
base::Thread::Options thread_options;
thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
DCHECK(g_quic_server_thread->StartWithOptions(thread_options));
base::FilePath test_files_root(
base::android::ConvertJavaStringToUTF8(env, jtest_files_root));
g_quic_server_thread->task_runner()->PostTask(
FROM_HERE, base::Bind(&ServeFilesFromDirectory, test_files_root));
}
void ShutdownQuicTestServer(JNIEnv* env, jclass jcaller) {
DCHECK(!g_quic_server_thread->task_runner()->BelongsToCurrentThread());
g_quic_server_thread->task_runner()->PostTask(
FROM_HERE, base::Bind(&ShutdownOnServerThread));
delete g_quic_server_thread;
}
jstring GetServerHost(JNIEnv* env, jclass jcaller) {
return base::android::ConvertUTF8ToJavaString(env, kServerHost).Release();
}
int GetServerPort(JNIEnv* env, jclass jcaller) {
return kServerPort;
}
bool RegisterQuicTestServer(JNIEnv* env) {
return RegisterNativesImpl(env);
}
} // namespace cronet
// Copyright 2015 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 COMPONENTS_CRONET_ANDROID_TEST_QUIC_TEST_SERVER_H_
#define COMPONENTS_CRONET_ANDROID_TEST_QUIC_TEST_SERVER_H_
#include <jni.h>
namespace cronet {
bool RegisterQuicTestServer(JNIEnv* env);
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_TEST_QUIC_TEST_SERVER_H_
// Copyright 2015 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.
package org.chromium.net;
import android.content.Context;
import org.chromium.base.JNINamespace;
/**
* Wrapper class to start a Quic test server.
*/
@JNINamespace("cronet")
public final class QuicTestServer {
public static void startQuicTestServer(Context context) {
nativeStartQuicTestServer(TestFilesInstaller.getInstalledPath(context));
}
public static void shutdownQuicTestServer() {
nativeShutdownQuicTestServer();
}
public static String getServerURL() {
return "http://" + getServerHost() + ":" + getServerPort();
}
public static String getServerHost() {
return nativeGetServerHost();
}
public static int getServerPort() {
return nativeGetServerPort();
}
private static native void nativeStartQuicTestServer(String filePath);
private static native void nativeShutdownQuicTestServer();
private static native String nativeGetServerHost();
private static native int nativeGetServerPort();
}
...@@ -9,9 +9,11 @@ import android.content.res.AssetManager; ...@@ -9,9 +9,11 @@ import android.content.res.AssetManager;
import android.util.Log; import android.util.Log;
import org.chromium.base.PathUtils; import org.chromium.base.PathUtils;
import org.chromium.base.annotations.SuppressFBWarnings;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
...@@ -30,14 +32,19 @@ public final class TestFilesInstaller { ...@@ -30,14 +32,19 @@ public final class TestFilesInstaller {
if (areFilesInstalled(context)) { if (areFilesInstalled(context)) {
return; return;
} }
install(context); try {
install(context, TEST_FILE_ASSET_PATH);
} catch (IOException e) {
// Make the test app crash and fail early.
throw new RuntimeException(e);
}
} }
/** /**
* Returns the installed path of the test files. * Returns the installed path of the test files.
*/ */
public static String getInstalledPath(Context context) { public static String getInstalledPath(Context context) {
return PathUtils.getDataDirectory(context) + "/test"; return PathUtils.getDataDirectory(context) + "/" + TEST_FILE_ASSET_PATH;
} }
/** /**
...@@ -51,34 +58,34 @@ public final class TestFilesInstaller { ...@@ -51,34 +58,34 @@ public final class TestFilesInstaller {
} }
/** /**
* Installs test files that are included in assets. * Installs test files that are included in {@code path}.
* @params context Application context * @params context Application context
* @params path
*/ */
private static void install(Context context) { private static void install(Context context, String path) throws IOException {
AssetManager assetManager = context.getAssets(); AssetManager assetManager = context.getAssets();
try { String files[] = assetManager.list(path);
String[] files = assetManager.list(TEST_FILE_ASSET_PATH); Log.i(TAG, "Loading " + path + " ...");
String destDir = getInstalledPath(context); String root = PathUtils.getDataDirectory(context);
File destDirFile = new File(destDir); if (files.length == 0) {
if (!destDirFile.mkdir()) { // The path is a file, so copy the file now.
throw new IllegalStateException( copyTestFile(assetManager, path, root + "/" + path);
"directory exists or it cannot be created."); } else {
} // The path is a directory, so recursively handle its files, since
Log.i(TAG, "Begin loading " + files.length + " test files."); // the directory can contain subdirectories.
for (String fileName : files) { String fullPath = root + "/" + path;
Log.i(TAG, "Loading " + fileName); File dir = new File(fullPath);
String destFilePath = destDir + "/" + fileName; if (!dir.exists()) {
if (!copyTestFile(assetManager, Log.i(TAG, "Creating directory " + fullPath + " ...");
TEST_FILE_ASSET_PATH + "/" + fileName, if (!dir.mkdir()) {
destFilePath)) { throw new IOException("Directory not created.");
Log.e(TAG, "Loading " + fileName + " failed.");
} }
} }
} catch (Exception e) { for (int i = 0; i < files.length; i++) {
e.printStackTrace(); install(context, path + "/" + files[i]);
}
} }
} }
/** /**
* Copies a file from assets to the device's file system. * Copies a file from assets to the device's file system.
* @param assetManager AssetManager of the application. * @param assetManager AssetManager of the application.
...@@ -86,41 +93,24 @@ public final class TestFilesInstaller { ...@@ -86,41 +93,24 @@ public final class TestFilesInstaller {
* @param destFilePath the destination file path. * @param destFilePath the destination file path.
* @throws IllegalStateException if the destination file already exists. * @throws IllegalStateException if the destination file already exists.
*/ */
private static boolean copyTestFile(AssetManager assetManager, @SuppressFBWarnings("OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE")
String srcFilePath, private static void copyTestFile(AssetManager assetManager,
String destFilePath) { String srcFilePath,
OutputStream out; String destFilePath) throws IOException {
try { File destFile = new File(destFilePath);
File destFile = new File(destFilePath); if (destFile.exists()) {
if (destFile.exists()) { throw new IllegalStateException(srcFilePath + " already exists");
throw new IllegalStateException(srcFilePath
+ " already exists");
}
out = new FileOutputStream(destFilePath);
} catch (Exception e) {
e.printStackTrace();
return false;
} }
try { OutputStream out = new FileOutputStream(destFilePath);
InputStream in = assetManager.open(srcFilePath); InputStream in = assetManager.open(srcFilePath);
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int read; int read;
while ((read = in.read(buffer)) != -1) { while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read); out.write(buffer, 0, read);
}
in.close();
out.flush();
out.close();
return true;
} catch (Exception e) {
try {
out.close();
} catch (Exception closeException) {
closeException.printStackTrace();
}
e.printStackTrace();
return false;
} }
in.close();
out.flush();
out.close();
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment