| Index: examples/dart/netcat/main.dart
|
| diff --git a/examples/dart/netcat/main.dart b/examples/dart/netcat/main.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..629ae9f2a5a60a43bf523822d642a7d14fa67788
|
| --- /dev/null
|
| +++ b/examples/dart/netcat/main.dart
|
| @@ -0,0 +1,212 @@
|
| +// Copyright 2014 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:async';
|
| +import 'dart:core';
|
| +import 'dart:typed_data';
|
| +
|
| +import 'package:mojo/public/dart/application.dart';
|
| +import 'package:mojo/public/dart/bindings.dart';
|
| +import 'package:mojo/public/dart/core.dart';
|
| +import 'package:mojo/services/files/public/interfaces/file.mojom.dart' as files;
|
| +import 'package:mojo/services/files/public/interfaces/types.mojom.dart'
|
| + as files;
|
| +import 'package:mojo/services/network/public/interfaces/net_address.mojom.dart';
|
| +import 'package:mojo/services/network/public/interfaces/network_error.mojom.dart';
|
| +import 'package:mojo/services/network/public/interfaces/network_service.mojom.dart';
|
| +import 'package:mojo/services/network/public/interfaces/tcp_bound_socket.mojom.dart';
|
| +import 'package:mojo/services/network/public/interfaces/tcp_connected_socket.mojom.dart';
|
| +import 'package:mojo/services/terminal/public/interfaces/terminal_client.mojom.dart';
|
| +
|
| +void ignoreFuture(Future f) {
|
| + f.catchError((e) {});
|
| +}
|
| +
|
| +NetAddress makeIPv4NetAddress(List<int> addr, int port) {
|
| + var rv = new NetAddress();
|
| + rv.family = NetAddressFamily_IPV4;
|
| + rv.ipv4 = new NetAddressIPv4();
|
| + rv.ipv4.addr = new List<int>.from(addr);
|
| + rv.ipv4.port = port;
|
| + return rv;
|
| +}
|
| +
|
| +void fputs(files.File f, String s) {
|
| + ignoreFuture(f.write((s + '\n').codeUnits, 0, files.Whence_FROM_CURRENT));
|
| +}
|
| +
|
| +// Connects the terminal |File| and the socket.
|
| +// TODO(vtl):
|
| +// * Error handling: both connection/socket errors and terminal errors.
|
| +// * Relatedly, we should listen for _socketSender's peer being closed (also
|
| +// _socket, I guess).
|
| +// * Handle the socket send pipe being full (currently, we assume it's never
|
| +// full).
|
| +class Connector {
|
| + final Application _application;
|
| + final files.FileProxy _terminal;
|
| + TcpConnectedSocketProxy _socket;
|
| + MojoDataPipeProducer _socketSender;
|
| + MojoDataPipeConsumer _socketReceiver;
|
| + MojoEventStream _socketReceiverEventStream;
|
| + final ByteData _readBuffer;
|
| + final ByteData _writeBuffer;
|
| +
|
| + // TODO(vtl): Don't just hard-code buffer sizes.
|
| + Connector(this._application, this._terminal)
|
| + : _readBuffer = new ByteData(16 * 1024),
|
| + _writeBuffer = new ByteData(16 * 1024);
|
| +
|
| + Future connect(NetAddress remote_address) async {
|
| + var networkService = new NetworkServiceProxy.unbound();
|
| + _application.connectToService('mojo:network_service', networkService);
|
| +
|
| + NetAddress local_address = makeIPv4NetAddress([0, 0, 0, 0], 0);
|
| + var boundSocket = new TcpBoundSocketProxy.unbound();
|
| + await networkService.ptr.createTcpBoundSocket(local_address, boundSocket);
|
| + await networkService.close();
|
| +
|
| + var sendDataPipe = new MojoDataPipe();
|
| + _socketSender = sendDataPipe.producer;
|
| + var receiveDataPipe = new MojoDataPipe();
|
| + _socketReceiver = receiveDataPipe.consumer;
|
| + _socket = new TcpConnectedSocketProxy.unbound();
|
| + await boundSocket.ptr.connect(remote_address, sendDataPipe.consumer,
|
| + receiveDataPipe.producer, _socket);
|
| + await boundSocket.close();
|
| +
|
| + // Set up reading from the terminal.
|
| + _startReadingFromTerminal();
|
| +
|
| + // Set up reading from the socket.
|
| + _socketReceiverEventStream = new MojoEventStream(_socketReceiver.handle);
|
| + _socketReceiverEventStream.listen(_onSocketReceiverEvent);
|
| + }
|
| +
|
| + void _startReadingFromTerminal() {
|
| + // TODO(vtl): Handle terminal errors.
|
| + _terminal.ptr
|
| + .read(_writeBuffer.lengthInBytes, 0, files.Whence_FROM_CURRENT)
|
| + .then(_onReadFromTerminal);
|
| + }
|
| +
|
| + void _onReadFromTerminal(files.FileReadResponseParams p) {
|
| + if (p.error != files.Error_OK) {
|
| + // TODO(vtl): Do terminal errors.
|
| + return;
|
| + }
|
| +
|
| + // TODO(vtl): Temporary hack: echo, since we don't have built-in echo
|
| + // support.
|
| + ignoreFuture(
|
| + _terminal.ptr.write(p.bytesRead, 0, files.Whence_FROM_CURRENT));
|
| +
|
| + // TODO(vtl): Verify that |bytesRead.length| is within the expected range.
|
| + for (var i = 0, j = 0; i < p.bytesRead.length; i++, j++) {
|
| + // TODO(vtl): Temporary hack: Translate \r to \n, since we don't have
|
| + // built-in support for that.
|
| + if (p.bytesRead[i] == 13) {
|
| + _writeBuffer.setUint8(i, 10);
|
| + } else {
|
| + _writeBuffer.setUint8(i, p.bytesRead[i]);
|
| + }
|
| + }
|
| +
|
| + // TODO(vtl): Handle the send data pipe being full (or closed).
|
| + _socketSender
|
| + .write(new ByteData.view(_writeBuffer.buffer, 0, p.bytesRead.length));
|
| +
|
| + _startReadingFromTerminal();
|
| + }
|
| +
|
| + void _onSocketReceiverEvent(List<int> event) {
|
| + var mojoSignals = new MojoHandleSignals(event[1]);
|
| + var shouldClose = false;
|
| + if (mojoSignals.isReadable) {
|
| + var numBytesRead = _socketReceiver.read(_readBuffer);
|
| + if (_socketReceiver.status.isOk) {
|
| + assert(numBytesRead > 0);
|
| + _terminal.ptr.write(_readBuffer.buffer.asUint8List(0, numBytesRead), 0,
|
| + files.Whence_FROM_CURRENT);
|
| + _socketReceiverEventStream.enableReadEvents();
|
| + } else {
|
| + shouldClose = true;
|
| + }
|
| + } else if (mojoSignals.isPeerClosed) {
|
| + shouldClose = true;
|
| + } else {
|
| + throw 'Unexpected handle event: $mojoSignals';
|
| + }
|
| + if (shouldClose) {
|
| + _socketReceiverEventStream.close();
|
| + _socketReceiverEventStream = null;
|
| + fputs(_terminal.ptr, 'Connection closed.');
|
| + }
|
| + }
|
| +}
|
| +
|
| +class TerminalClientImpl implements TerminalClient {
|
| + TerminalClientStub _stub;
|
| + Application _application;
|
| + String _resolvedUrl;
|
| +
|
| + TerminalClientImpl(
|
| + this._application, this._resolvedUrl, MojoMessagePipeEndpoint endpoint) {
|
| + _stub = new TerminalClientStub.fromEndpoint(endpoint, this);
|
| + }
|
| +
|
| + @override
|
| + void connectToTerminal(files.FileProxy terminal) {
|
| + var url = Uri.parse(_resolvedUrl);
|
| + NetAddress remote_address;
|
| + try {
|
| + remote_address = _getNetAddressFromUrl(url);
|
| + } catch (e) {
|
| + fputs(terminal.ptr, 'HALP: Add a query: ?host=<host>&port=<port>\n'
|
| + '(<host> must be "localhost" or n1.n2.n3.n4)\n\n'
|
| + 'Got query parameters:\n' + url.queryParameters.toString());
|
| + ignoreFuture(terminal.close());
|
| + return;
|
| + }
|
| +
|
| + // TODO(vtl): Currently, we only do IPv4, so this should work.
|
| + fputs(terminal.ptr, 'Connecting to: ' +
|
| + remote_address.ipv4.addr.join('.') +
|
| + ':' +
|
| + remote_address.ipv4.port.toString() +
|
| + '...');
|
| +
|
| + var connector = new Connector(_application, terminal);
|
| + connector.connect(remote_address);
|
| + }
|
| +
|
| + // Note: May throw all sorts of things.
|
| + static NetAddress _getNetAddressFromUrl(Uri url) {
|
| + var params = url.queryParameters;
|
| + var host = params['host'];
|
| + return makeIPv4NetAddress(
|
| + (host == 'localhost') ? [127, 0, 0, 1] : Uri.parseIPv4Address(host),
|
| + int.parse(params['port']));
|
| + }
|
| +}
|
| +
|
| +class NetcatApplication extends Application {
|
| + NetcatApplication.fromHandle(MojoHandle handle) : super.fromHandle(handle);
|
| +
|
| + @override
|
| + void acceptConnection(String requestorUrl, String resolvedUrl,
|
| + ApplicationConnection connection) {
|
| + connection.provideService(TerminalClientName,
|
| + (endpoint) => new TerminalClientImpl(this, resolvedUrl, endpoint));
|
| + }
|
| +}
|
| +
|
| +main(List args) {
|
| + MojoHandle appHandle = new MojoHandle(args[0]);
|
| + String url = args[1];
|
| + new NetcatApplication.fromHandle(appHandle)
|
| + ..onError = (() {
|
| + assert(MojoHandle.reportLeakedHandles());
|
| + });
|
| +}
|
|
|