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 @@
'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/NetworkChangeNotifierUtil.java',
'cronet/android/test/src/org/chromium/net/QuicTestServer.java',
'cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java',
],
'variables': {
......@@ -277,6 +278,8 @@
'cronet/android/test/mock_url_request_job_factory.h',
'cronet/android/test/native_test_server.cc',
'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.h',
'cronet/android/test/network_change_notifier_util.cc',
......@@ -287,6 +290,7 @@
'cronet_tests_jni_headers',
'../base/base.gyp:base',
'../net/net.gyp:net',
'../net/net.gyp:net_quic_proto',
'../net/net.gyp:net_test_support',
'../net/net.gyp:simple_quic_tools',
'../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 @@
#include "mock_url_request_job_factory.h"
#include "native_test_server.h"
#include "network_change_notifier_util.h"
#include "quic_test_server.h"
#include "test_upload_data_stream_handler.h"
namespace {
const base::android::RegistrationMethod kCronetTestsRegisteredMethods[] = {
{"MockUrlRequestJobFactory", cronet::RegisterMockUrlRequestJobFactory},
{"RegisterNativeTestServer", cronet::RegisterNativeTestServer},
{"NativeTestServer", cronet::RegisterNativeTestServer},
{"QuicTestServer", cronet::RegisterQuicTestServer},
{"NetworkChangeNotifierUtil", cronet::RegisterNetworkChangeNotifierUtil},
{"TestUploadDataStreamHandlerRegisterJni",
cronet::TestUploadDataStreamHandlerRegisterJni},
......
......@@ -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
@Feature({"Cronet"})
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;
import android.util.Log;
import org.chromium.base.PathUtils;
import org.chromium.base.annotations.SuppressFBWarnings;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
......@@ -30,14 +32,19 @@ public final class TestFilesInstaller {
if (areFilesInstalled(context)) {
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.
*/
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 {
}
/**
* Installs test files that are included in assets.
* Installs test files that are included in {@code path}.
* @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();
try {
String[] files = assetManager.list(TEST_FILE_ASSET_PATH);
String destDir = getInstalledPath(context);
File destDirFile = new File(destDir);
if (!destDirFile.mkdir()) {
throw new IllegalStateException(
"directory exists or it cannot be created.");
}
Log.i(TAG, "Begin loading " + files.length + " test files.");
for (String fileName : files) {
Log.i(TAG, "Loading " + fileName);
String destFilePath = destDir + "/" + fileName;
if (!copyTestFile(assetManager,
TEST_FILE_ASSET_PATH + "/" + fileName,
destFilePath)) {
Log.e(TAG, "Loading " + fileName + " failed.");
String files[] = assetManager.list(path);
Log.i(TAG, "Loading " + path + " ...");
String root = PathUtils.getDataDirectory(context);
if (files.length == 0) {
// The path is a file, so copy the file now.
copyTestFile(assetManager, path, root + "/" + path);
} else {
// The path is a directory, so recursively handle its files, since
// the directory can contain subdirectories.
String fullPath = root + "/" + path;
File dir = new File(fullPath);
if (!dir.exists()) {
Log.i(TAG, "Creating directory " + fullPath + " ...");
if (!dir.mkdir()) {
throw new IOException("Directory not created.");
}
}
} catch (Exception e) {
e.printStackTrace();
for (int i = 0; i < files.length; i++) {
install(context, path + "/" + files[i]);
}
}
}
/**
* Copies a file from assets to the device's file system.
* @param assetManager AssetManager of the application.
......@@ -86,41 +93,24 @@ public final class TestFilesInstaller {
* @param destFilePath the destination file path.
* @throws IllegalStateException if the destination file already exists.
*/
private static boolean copyTestFile(AssetManager assetManager,
String srcFilePath,
String destFilePath) {
OutputStream out;
try {
File destFile = new File(destFilePath);
if (destFile.exists()) {
throw new IllegalStateException(srcFilePath
+ " already exists");
}
out = new FileOutputStream(destFilePath);
} catch (Exception e) {
e.printStackTrace();
return false;
@SuppressFBWarnings("OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE")
private static void copyTestFile(AssetManager assetManager,
String srcFilePath,
String destFilePath) throws IOException {
File destFile = new File(destFilePath);
if (destFile.exists()) {
throw new IllegalStateException(srcFilePath + " already exists");
}
try {
InputStream in = assetManager.open(srcFilePath);
OutputStream out = new FileOutputStream(destFilePath);
InputStream in = assetManager.open(srcFilePath);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
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;
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
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