Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ Usage: webdev_proxy serve [-- [webdev serve arguments]]
--[no-]rewrite-404s Rewrite every request that returns a 404 to /index.html
(defaults to on)

The proxy arguments can contain either the webdev <directory>[:<port>] syntax
or proxy-specific <directory>:<proxyPort>:<port> syntax.

Run "webdev_proxy help" to see global options.

You may use any of the following options supported by `webdev serve` by passing them after the `--` separator.
Expand Down
49 changes: 30 additions & 19 deletions lib/src/serve_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,24 +138,27 @@ class ServeCommand extends Command<int> {
// Parse the hostname to serve each dir on (defaults to localhost).
final hostnameResults = parseHostname(argResults!.rest);
final hostname = hostnameResults.hostname;
final remainingArgs = hostnameResults.remainingArgs;

// Parse the directory:port mappings that will be used by the proxy servers.
// Each proxy will be mapped to a `webdev serve` instance on another port.
final portsToServeByDir = parseDirectoryArgs(argResults!.rest);
final dirResults = parseDirectoryArgs(hostnameResults.remainingArgs);

// Find open ports for each of the directories to be served by webdev.
final portsToProxyByDir = {
for (final dir in portsToServeByDir.keys) dir: await findUnusedPort()
};
final dirPorts = await Future.wait(dirResults.ports
.map((dirPort) async => DirectoryPorts(
directory: dirPort.directory,
proxyPort: dirPort.proxyPort ?? await findUnusedPort(),
servePort: dirPort.servePort))
.toList());

final webdevArgs = [
if (hostname != 'localhost') '--hostname=$hostname',
...dirResults.remainingArgs,
for (final dir in dirPorts) '${dir.directory}:${dir.proxyPort}',
];

// Start the underlying `webdev serve` process.
webdevServer = await WebdevServer.start([
if (hostname != 'localhost') '--hostname=$hostname',
...remainingArgs,
for (final dir in portsToServeByDir.keys)
'$dir:${portsToProxyByDir[dir]}',
]);
webdevServer = await WebdevServer.start(webdevArgs);

// Stop proxies and exit if webdev exits.
unawaited(webdevServer.exitCode.then((code) {
Expand All @@ -166,21 +169,19 @@ class ServeCommand extends Command<int> {
}));

// Start a proxy server for each directory.
for (final dir in portsToServeByDir.keys) {
for (final dirPort in dirPorts) {
try {
proxies.add(await WebdevProxyServer.start(
dir: dir,
dir: dirPort.directory,
hostname: hostname,
portToProxy: portsToProxyByDir[dir],
portToServe: portsToServeByDir[dir]!,
portToProxy: dirPort.proxyPort,
portToServe: dirPort.servePort,
rewrite404s: argResults![rewrite404sFlag] == true,
));
} catch (e, stackTrace) {
proxiesFailed = true;
log.severe(
'Failed to start proxy server on port ${portsToServeByDir[dir]}',
e,
stackTrace);
log.severe('Failed to start proxy server on port ${dirPort.servePort}',
e, stackTrace);
shutDown(ExitCode.unavailable.code);
break;
}
Expand All @@ -189,3 +190,13 @@ class ServeCommand extends Command<int> {
return exitCodeCompleter.future;
}
}

class DirectoryPorts {
final String directory;
final int proxyPort;
final int servePort;
DirectoryPorts(
{required this.directory,
required this.proxyPort,
required this.servePort});
}
50 changes: 35 additions & 15 deletions lib/src/webdev_arg_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,57 @@ final _defaultWebDirs = const ['web'];
final _dirPattern = RegExp(
// Matches and captures any directory path, e.g. `web` or `test/nested/dir/`
r'^([\w/]+)'
// Optional non-capturing group since webdev allows for the port to be omitted
// Optional non-capturing group since webdev allows for the ports to be omitted
r'(?:'
// Matches and captures any port, e.g. `:8080` or `:9001`
r':(\d+)'
// Ends the optional non-capturing group
r')?$');
r'){0,2}$');

class ParsedDirectoryPorts {
final String directory;
final int? proxyPort;
final int servePort;
ParsedDirectoryPorts(
{required this.directory, this.proxyPort, required this.servePort});
}

/// Returns a mapping of directories to ports parsed from command-line [args] in
/// the form of `<directory>:<port>`.
///
/// If no mappings are specified in [args], the default mapping of web:8080 is
/// returned.
Map<String, int> parseDirectoryArgs(List<String> args) {
final result = <String, int>{};
ParseDirectoryArgsResults parseDirectoryArgs(List<String> args) {
final ports = <ParsedDirectoryPorts>[];
final remainingArgs = <String>[];
var basePort = 8080;
final dirArgs = args.where((_dirPattern.hasMatch));
if (dirArgs.isEmpty) {
for (final dir in _defaultWebDirs) {
result[dir] = basePort++;
}
} else {
for (final arg in dirArgs) {
for (final arg in args) {
if (!_dirPattern.hasMatch(arg)) {
remainingArgs.add(arg);
} else {
final splitOption = arg.split(':');
if (splitOption.length == 2) {
result[splitOption.first] = int.parse(splitOption.last);
if (splitOption.length == 3) {
ports.add(ParsedDirectoryPorts(
directory: splitOption[0],
proxyPort: int.parse(splitOption[1]),
servePort: int.parse(splitOption[2])));
} else if (splitOption.length == 2) {
ports.add(ParsedDirectoryPorts(
directory: splitOption.first,
servePort: int.parse(splitOption.last)));
} else {
result[arg] = basePort++;
ports
.add(ParsedDirectoryPorts(directory: arg, servePort: basePort++));
}
}
}
return result;
return ParseDirectoryArgsResults(ports, remainingArgs);
}

class ParseDirectoryArgsResults {
final List<ParsedDirectoryPorts> ports;
final List<String> remainingArgs;
ParseDirectoryArgsResults(this.ports, this.remainingArgs);
}

/// Returns the value of the `--hostname` option from a list of command-line
Expand Down
Loading