Commit d86fb66e authored by Adam Barth's avatar Adam Barth

Merge pull request #35 from abarth/rm_http

Remove the --http option
parents e96a609d 11350020
......@@ -14,6 +14,9 @@ DART=dart
if [ "$FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]; then
(cd "$FLUTTER_TOOLS_DIR"; pub get)
if [ -f "$SNAPSHOT_PATH" ]; then
rm "$SNAPSHOT_PATH"
fi
fi
REVISION=`(cd "$FLUTTER_ROOT"; git rev-parse HEAD)`
......
// 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.
import 'dart:io';
import 'package:args/args.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_route/shelf_route.dart' as shelf_route;
import 'package:shelf_static/shelf_static.dart';
void printUsage(parser) {
print('Usage: sky_server [-v] PORT');
print(parser.usage);
}
void addRoute(var router, String route, String path) {
router.add(
route,
['GET', 'HEAD'],
createStaticHandler(
path,
serveFilesOutsidePath: true,
listDirectories: true
), exactMatch: false
);
}
main(List<String> argv) async {
ArgParser parser = new ArgParser();
parser.addFlag('help', abbr: 'h', negatable: false,
help: 'Display this help message.');
parser.addFlag('verbose', abbr: 'v', negatable: false,
help: 'Log requests to stdout.');
parser.addOption('route', allowMultiple: true, splitCommas: false,
help: 'Adds a virtual directory to the root.');
ArgResults args = parser.parse(argv);
if (args['help'] || args.rest.length != 1) {
printUsage(parser);
return;
}
int port;
try {
port = int.parse(args.rest[0]);
} catch(e) {
printUsage(parser);
return;
}
var router = shelf_route.router();
if (args['route'] != null) {
for (String arg in args['route']) {
List<String> parsedArgs = arg.split(',');
addRoute(router, parsedArgs[0], parsedArgs[1]);
}
}
addRoute(router, '/', Directory.current.path);
var handler = router.handler;
if (args['verbose'])
handler = const Pipeline().addMiddleware(logRequests()).addHandler(handler);
HttpServer server;
try {
server = await io.serve(handler, InternetAddress.LOOPBACK_IP_V4, port);
print('Serving ${Directory.current.absolute.path} from '
'http://${server.address.address}:${server.port}.');
} catch(e) {
print(e);
exit(1);
}
server.defaultResponseHeaders
..removeAll('x-content-type-options')
..removeAll('x-frame-options')
..removeAll('x-xss-protection')
..add('cache-control', 'no-store');
}
......@@ -9,6 +9,7 @@ import 'dart:typed_data';
import 'package:archive/archive.dart';
import 'package:flx/bundle.dart';
import 'package:flx/signing.dart';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
import '../toolchain.dart';
......@@ -147,6 +148,27 @@ class BuildCommand extends FlutterCommand {
);
}
Future<int> buildInTempDir({
String mainPath: _kDefaultMainPath,
void onBundleAvailable(String bundlePath)
}) async {
int result;
Directory tempDir = await Directory.systemTemp.createTemp('flutter_tools');
try {
String localBundlePath = path.join(tempDir.path, 'app.flx');
String localSnapshotPath = path.join(tempDir.path, 'snapshot_blob.bin');
result = await build(
snapshotPath: localSnapshotPath,
outputPath: localBundlePath,
mainPath: mainPath
);
onBundleAvailable(localBundlePath);
} finally {
tempDir.deleteSync(recursive: true);
}
return result;
}
Future<int> build({
String assetBase: _kDefaultAssetBase,
String mainPath: _kDefaultMainPath,
......
......@@ -34,7 +34,6 @@ class ListenCommand extends FlutterCommand {
help: 'Target app path or filename to start.');
}
static const String _localFlutterBundle = 'app.flx';
static const String _remoteFlutterBundle = 'Documents/app.flx';
@override
......@@ -53,25 +52,26 @@ class ListenCommand extends FlutterCommand {
BuildCommand builder = new BuildCommand();
builder.inheritFromParent(this);
builder.build(outputPath: _localFlutterBundle);
for (Device device in devices.all) {
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
if (package == null || !device.isConnected())
continue;
if (device is AndroidDevice) {
await devices.android.startServer(
argResults['target'], true, argResults['checked'], package);
} else if (device is IOSDevice) {
device.pushFile(package, _localFlutterBundle, _remoteFlutterBundle);
} else if (device is IOSSimulator) {
// TODO(abarth): Move pushFile up to Device once Android supports
// pushing new bundles.
device.pushFile(package, _localFlutterBundle, _remoteFlutterBundle);
} else {
assert(false);
await builder.buildInTempDir(
onBundleAvailable: (String localBundlePath) {
for (Device device in devices.all) {
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
if (package == null || !device.isConnected())
continue;
if (device is AndroidDevice) {
device.startBundle(package, localBundlePath, true, argResults['checked']);
} else if (device is IOSDevice) {
device.pushFile(package, localBundlePath, _remoteFlutterBundle);
} else if (device is IOSSimulator) {
// TODO(abarth): Move pushFile up to Device once Android supports
// pushing new bundles.
device.pushFile(package, localBundlePath, _remoteFlutterBundle);
} else {
assert(false);
}
}
}
}
);
if (singleRun || !watchDirectory())
break;
......
......@@ -16,8 +16,6 @@ import 'install.dart';
import 'stop.dart';
final Logger _logging = new Logger('sky_tools.start');
const String _localBundleName = 'app.flx';
const String _localSnapshotName = 'snapshot_blob.bin';
class StartCommand extends FlutterCommand {
final String name = 'start';
......@@ -35,9 +33,6 @@ class StartCommand extends FlutterCommand {
defaultsTo: '.',
abbr: 't',
help: 'Target app path or filename to start.');
argParser.addFlag('http',
negatable: true,
help: 'Use a local HTTP server to serve your app to your device.');
argParser.addFlag('boot',
help: 'Boot the iOS Simulator if it isn\'t already running.');
}
......@@ -69,30 +64,18 @@ class StartCommand extends FlutterCommand {
continue;
if (device is AndroidDevice) {
String target = path.absolute(argResults['target']);
if (argResults['http']) {
if (await device.startServer(target, poke, argResults['checked'], package))
startedSomething = true;
} else {
String mainPath = target;
if (FileSystemEntity.isDirectorySync(target))
mainPath = path.join(target, 'lib', 'main.dart');
BuildCommand builder = new BuildCommand();
builder.inheritFromParent(this);
Directory tempDir = await Directory.systemTemp.createTemp('flutter_tools');
try {
String localBundlePath = path.join(tempDir.path, _localBundleName);
String localSnapshotPath = path.join(tempDir.path, _localSnapshotName);
await builder.build(
snapshotPath: localSnapshotPath,
outputPath: localBundlePath,
mainPath: mainPath);
String mainPath = target;
if (FileSystemEntity.isDirectorySync(target))
mainPath = path.join(target, 'lib', 'main.dart');
BuildCommand builder = new BuildCommand();
builder.inheritFromParent(this);
await builder.buildInTempDir(
mainPath: mainPath,
onBundleAvailable: (String localBundlePath) {
if (device.startBundle(package, localBundlePath, poke, argResults['checked']))
startedSomething = true;
} finally {
tempDir.deleteSync(recursive: true);
}
}
);
} else {
if (await device.startApp(package))
startedSomething = true;
......
......@@ -3,9 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:crypto/crypto.dart';
import 'package:logging/logging.dart';
......@@ -13,7 +11,6 @@ import 'package:path/path.dart' as path;
import 'application_package.dart';
import 'build_configuration.dart';
import 'os_utils.dart';
import 'process.dart';
final Logger _logging = new Logger('sky_tools.device');
......@@ -507,14 +504,10 @@ class IOSSimulator extends Device {
class AndroidDevice extends Device {
static const String _ADB_PATH = 'adb';
static const int _observatoryPort = 8181;
static const int _serverPort = 9888;
static const String className = 'AndroidDevice';
static final String defaultDeviceID = 'default_android_device';
static const String _kFlutterServerStartMessage = 'Serving';
static const Duration _kFlutterServerTimeout = const Duration(seconds: 3);
String productID;
String modelID;
String deviceCodeName;
......@@ -718,13 +711,6 @@ class AndroidDevice extends Device {
return CryptoUtils.bytesToHex(sha1.close());
}
/**
* Since Window's paths have backslashes, we need to convert those to forward slashes to make a valid URL
*/
String _convertToURL(String path) {
return path.replaceAll('\\', '/');
}
@override
bool isAppInstalled(ApplicationPackage app) {
if (!isConnected()) {
......@@ -793,81 +779,16 @@ class AndroidDevice extends Device {
return true;
}
Future<bool> startServer(
String target, bool poke, bool checked, AndroidApk apk) async {
String serverRoot = '';
String mainDart = '';
String missingMessage = '';
if (FileSystemEntity.isDirectorySync(target)) {
serverRoot = target;
mainDart = path.join(serverRoot, 'lib', 'main.dart');
missingMessage = 'Missing lib/main.dart in project: $serverRoot';
} else {
serverRoot = Directory.current.path;
mainDart = target;
missingMessage = '$mainDart does not exist.';
}
if (!FileSystemEntity.isFileSync(mainDart)) {
_logging.severe(missingMessage);
return false;
}
if (!poke) {
_forwardObservatoryPort();
// Actually start the server.
Process server = await Process.start(
sdkBinaryName('pub'), ['run', 'sky_tools:sky_server', _serverPort.toString()],
workingDirectory: serverRoot,
mode: ProcessStartMode.DETACHED_WITH_STDIO
);
await server.stdout.transform(UTF8.decoder)
.firstWhere((String value) => value.startsWith(_kFlutterServerStartMessage))
.timeout(_kFlutterServerTimeout);
// Set up reverse port-forwarding so that the Android app can reach the
// server running on localhost.
String serverPortString = 'tcp:$_serverPort';
runCheckedSync(adbCommandForDevice(['reverse', serverPortString, serverPortString]));
}
String relativeDartMain = _convertToURL(path.relative(mainDart, from: serverRoot));
String url = 'http://localhost:$_serverPort/$relativeDartMain';
if (poke)
url += '?rand=${new Random().nextDouble()}';
// Actually launch the app on Android.
List<String> cmd = adbCommandForDevice([
'shell', 'am', 'start',
'-a', 'android.intent.action.VIEW',
'-d', url,
]);
if (checked)
cmd.addAll(['--ez', 'enable-checked-mode', 'true']);
cmd.add(apk.launchActivity);
runCheckedSync(cmd);
return true;
}
@override
Future<bool> startApp(ApplicationPackage app) async {
// Android currently has to be started with startServer(...).
// Android currently has to be started with startBundle(...).
assert(false);
return false;
}
Future<bool> stopApp(ApplicationPackage app) async {
final AndroidApk apk = app;
// Turn off reverse port forwarding
runSync(adbCommandForDevice(['reverse', '--remove', 'tcp:$_serverPort']));
// Stop the app
runSync(adbCommandForDevice(['shell', 'am', 'force-stop', apk.id]));
// Kill the server
osUtils.killTcpPortListeners(_serverPort);
return true;
}
......
......@@ -6,8 +6,6 @@ import 'dart:io';
import 'package:logging/logging.dart';
import 'process.dart';
final OperatingSystemUtils osUtils = new OperatingSystemUtils._();
final Logger _logging = new Logger('sky_tools.os');
......@@ -16,21 +14,16 @@ abstract class OperatingSystemUtils {
factory OperatingSystemUtils._() {
if (Platform.isWindows) {
return new _WindowsUtils();
} else if (Platform.isMacOS) {
return new _MacUtils();
} else {
return new _LinuxUtils();
return new _PosixUtils();
}
}
/// Make the given file executable. This may be a no-op on some platforms.
ProcessResult makeExecutable(File file);
/// A best-effort attempt to kill all listeners on the given TCP port.
void killTcpPortListeners(int tcpPort);
}
abstract class _PosixUtils implements OperatingSystemUtils {
class _PosixUtils implements OperatingSystemUtils {
ProcessResult makeExecutable(File file) {
return Process.runSync('chmod', ['u+x', file.path]);
}
......@@ -41,51 +34,4 @@ class _WindowsUtils implements OperatingSystemUtils {
ProcessResult makeExecutable(File file) {
return new ProcessResult(0, 0, null, null);
}
void killTcpPortListeners(int tcpPort) {
// Get list of network processes and split on newline
List<String> processes = runSync(['netstat.exe','-ano']).split("\r");
// List entries from netstat is formatted like so:
// TCP 192.168.2.11:50945 192.30.252.90:443 LISTENING 1304
// This regexp is to find process where the the port exactly matches
RegExp pattern = new RegExp(':$tcpPort[ ]+');
// Split the columns by 1 or more spaces
RegExp columnPattern = new RegExp('[ ]+');
processes.forEach((String process) {
if (process.contains(pattern)) {
// The last column is the Process ID
String processId = process.split(columnPattern).last;
// Force and Tree kill the process
_logging.info('kill $processId');
runSync(['TaskKill.exe', '/F', '/T', '/PID', processId]);
}
});
}
}
class _MacUtils extends _PosixUtils {
void killTcpPortListeners(int tcpPort) {
String pids = runSync(['lsof', '-i', ':$tcpPort', '-t']).trim();
if (pids.isNotEmpty) {
// Handle multiple returned pids.
for (String pidString in pids.split('\n')) {
// Killing a pid with a shell command from within dart is hard, so use a
// library command, but it's still nice to give the equivalent command
// when doing verbose logging.
_logging.info('kill $pidString');
int pid = int.parse(pidString, onError: (_) => null);
if (pid != null)
Process.killPid(pid);
}
}
}
}
class _LinuxUtils extends _PosixUtils {
void killTcpPortListeners(int tcpPort) {
runSync(['fuser', '-k', '$tcpPort/tcp']);
}
}
......@@ -15,9 +15,6 @@ dependencies:
crypto: ^0.9.1
mustache4dart: ^1.0.0
path: ^1.3.0
shelf_route: ^0.13.4
shelf_static: ^0.2.3
shelf: ^0.6.2
stack_trace: ^1.4.0
test: ^0.12.5
yaml: ^2.1.3
......
......@@ -34,33 +34,5 @@ defineTests() {
expect(mode.substring(0, 3), endsWith('x'));
}
});
/// Start a script listening on a port, try and kill that process.
test('killTcpPortListeners', () async {
final int port = 40170;
File file = new File(p.join(temp.path, 'script.dart'));
file.writeAsStringSync('''
import 'dart:io';
void main() async {
ServerSocket serverSocket = await ServerSocket.bind(
InternetAddress.LOOPBACK_IP_V4, ${port});
// wait...
print('listening on port ${port}...');
}
''');
Process process = await Process.start('dart', [file.path]);
await process.stdout.first;
osUtils.killTcpPortListeners(40170);
int exitCode = await process.exitCode;
expect(exitCode, isNot(equals(0)));
});
/// Try and kill with a port that no process is listening to.
test('killTcpPortListeners none', () {
osUtils.killTcpPortListeners(40171);
});
});
}
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