| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | 2 // Use of this source code is governed by a BSD-style license that can be | 
|  | 3 // found in the LICENSE file. | 
|  | 4 | 
|  | 5 import 'dart:async'; | 
|  | 6 import 'dart:core'; | 
|  | 7 import 'dart:typed_data'; | 
|  | 8 | 
|  | 9 import 'package:mojo/public/dart/application.dart'; | 
|  | 10 import 'package:mojo/public/dart/bindings.dart'; | 
|  | 11 import 'package:mojo/public/dart/core.dart'; | 
|  | 12 import 'package:mojo/services/files/public/interfaces/file.mojom.dart' as files; | 
|  | 13 import 'package:mojo/services/files/public/interfaces/types.mojom.dart' | 
|  | 14     as files; | 
|  | 15 import 'package:mojo/services/network/public/interfaces/net_address.mojom.dart'; | 
|  | 16 import 'package:mojo/services/network/public/interfaces/network_error.mojom.dart
     '; | 
|  | 17 import 'package:mojo/services/network/public/interfaces/network_service.mojom.da
     rt'; | 
|  | 18 import 'package:mojo/services/network/public/interfaces/tcp_bound_socket.mojom.d
     art'; | 
|  | 19 import 'package:mojo/services/network/public/interfaces/tcp_connected_socket.moj
     om.dart'; | 
|  | 20 import 'package:mojo/services/terminal/public/interfaces/terminal_client.mojom.d
     art'; | 
|  | 21 | 
|  | 22 void ignoreFuture(Future f) { | 
|  | 23   f.catchError((e) {}); | 
|  | 24 } | 
|  | 25 | 
|  | 26 NetAddress makeIPv4NetAddress(List<int> addr, int port) { | 
|  | 27   var rv = new NetAddress(); | 
|  | 28   rv.family = NetAddressFamily_IPV4; | 
|  | 29   rv.ipv4 = new NetAddressIPv4(); | 
|  | 30   rv.ipv4.addr = new List<int>.from(addr); | 
|  | 31   rv.ipv4.port = port; | 
|  | 32   return rv; | 
|  | 33 } | 
|  | 34 | 
|  | 35 void fputs(files.File f, String s) { | 
|  | 36   ignoreFuture(f.write((s + '\n').codeUnits, 0, files.Whence_FROM_CURRENT)); | 
|  | 37 } | 
|  | 38 | 
|  | 39 // Connects the terminal |File| and the socket. | 
|  | 40 // TODO(vtl): | 
|  | 41 // * Error handling: both connection/socket errors and terminal errors. | 
|  | 42 // * Relatedly, we should listen for _socketSender's peer being closed (also | 
|  | 43 //   _socket, I guess). | 
|  | 44 // * Handle the socket send pipe being full (currently, we assume it's never | 
|  | 45 //   full). | 
|  | 46 class Connector { | 
|  | 47   final Application _application; | 
|  | 48   final files.FileProxy _terminal; | 
|  | 49   TcpConnectedSocketProxy _socket; | 
|  | 50   MojoDataPipeProducer _socketSender; | 
|  | 51   MojoDataPipeConsumer _socketReceiver; | 
|  | 52   MojoEventStream _socketReceiverEventStream; | 
|  | 53   final ByteData _readBuffer; | 
|  | 54   final ByteData _writeBuffer; | 
|  | 55 | 
|  | 56   // TODO(vtl): Don't just hard-code buffer sizes. | 
|  | 57   Connector(this._application, this._terminal) | 
|  | 58       : _readBuffer = new ByteData(16 * 1024), | 
|  | 59         _writeBuffer = new ByteData(16 * 1024); | 
|  | 60 | 
|  | 61   Future connect(NetAddress remote_address) async { | 
|  | 62     var networkService = new NetworkServiceProxy.unbound(); | 
|  | 63     _application.connectToService('mojo:network_service', networkService); | 
|  | 64 | 
|  | 65     NetAddress local_address = makeIPv4NetAddress([0, 0, 0, 0], 0); | 
|  | 66     var boundSocket = new TcpBoundSocketProxy.unbound(); | 
|  | 67     await networkService.ptr.createTcpBoundSocket(local_address, boundSocket); | 
|  | 68     await networkService.close(); | 
|  | 69 | 
|  | 70     var sendDataPipe = new MojoDataPipe(); | 
|  | 71     _socketSender = sendDataPipe.producer; | 
|  | 72     var receiveDataPipe = new MojoDataPipe(); | 
|  | 73     _socketReceiver = receiveDataPipe.consumer; | 
|  | 74     _socket = new TcpConnectedSocketProxy.unbound(); | 
|  | 75     await boundSocket.ptr.connect(remote_address, sendDataPipe.consumer, | 
|  | 76         receiveDataPipe.producer, _socket); | 
|  | 77     await boundSocket.close(); | 
|  | 78 | 
|  | 79     // Set up reading from the terminal. | 
|  | 80     _startReadingFromTerminal(); | 
|  | 81 | 
|  | 82     // Set up reading from the socket. | 
|  | 83     _socketReceiverEventStream = new MojoEventStream(_socketReceiver.handle); | 
|  | 84     _socketReceiverEventStream.listen(_onSocketReceiverEvent); | 
|  | 85   } | 
|  | 86 | 
|  | 87   void _startReadingFromTerminal() { | 
|  | 88     // TODO(vtl): Handle terminal errors. | 
|  | 89     _terminal.ptr | 
|  | 90         .read(_writeBuffer.lengthInBytes, 0, files.Whence_FROM_CURRENT) | 
|  | 91         .then(_onReadFromTerminal); | 
|  | 92   } | 
|  | 93 | 
|  | 94   void _onReadFromTerminal(files.FileReadResponseParams p) { | 
|  | 95     if (p.error != files.Error_OK) { | 
|  | 96       // TODO(vtl): Do terminal errors. | 
|  | 97       return; | 
|  | 98     } | 
|  | 99 | 
|  | 100     // TODO(vtl): Temporary hack: echo, since we don't have built-in echo | 
|  | 101     // support. | 
|  | 102     ignoreFuture( | 
|  | 103         _terminal.ptr.write(p.bytesRead, 0, files.Whence_FROM_CURRENT)); | 
|  | 104 | 
|  | 105     // TODO(vtl): Verify that |bytesRead.length| is within the expected range. | 
|  | 106     for (var i = 0, j = 0; i < p.bytesRead.length; i++, j++) { | 
|  | 107       // TODO(vtl): Temporary hack: Translate \r to \n, since we don't have | 
|  | 108       // built-in support for that. | 
|  | 109       if (p.bytesRead[i] == 13) { | 
|  | 110         _writeBuffer.setUint8(i, 10); | 
|  | 111       } else { | 
|  | 112         _writeBuffer.setUint8(i, p.bytesRead[i]); | 
|  | 113       } | 
|  | 114     } | 
|  | 115 | 
|  | 116     // TODO(vtl): Handle the send data pipe being full (or closed). | 
|  | 117     _socketSender | 
|  | 118         .write(new ByteData.view(_writeBuffer.buffer, 0, p.bytesRead.length)); | 
|  | 119 | 
|  | 120     _startReadingFromTerminal(); | 
|  | 121   } | 
|  | 122 | 
|  | 123   void _onSocketReceiverEvent(List<int> event) { | 
|  | 124     var mojoSignals = new MojoHandleSignals(event[1]); | 
|  | 125     var shouldClose = false; | 
|  | 126     if (mojoSignals.isReadable) { | 
|  | 127       var numBytesRead = _socketReceiver.read(_readBuffer); | 
|  | 128       if (_socketReceiver.status.isOk) { | 
|  | 129         assert(numBytesRead > 0); | 
|  | 130         _terminal.ptr.write(_readBuffer.buffer.asUint8List(0, numBytesRead), 0, | 
|  | 131             files.Whence_FROM_CURRENT); | 
|  | 132         _socketReceiverEventStream.enableReadEvents(); | 
|  | 133       } else { | 
|  | 134         shouldClose = true; | 
|  | 135       } | 
|  | 136     } else if (mojoSignals.isPeerClosed) { | 
|  | 137       shouldClose = true; | 
|  | 138     } else { | 
|  | 139       throw 'Unexpected handle event: $mojoSignals'; | 
|  | 140     } | 
|  | 141     if (shouldClose) { | 
|  | 142       _socketReceiverEventStream.close(); | 
|  | 143       _socketReceiverEventStream = null; | 
|  | 144       fputs(_terminal.ptr, 'Connection closed.'); | 
|  | 145     } | 
|  | 146   } | 
|  | 147 } | 
|  | 148 | 
|  | 149 class TerminalClientImpl implements TerminalClient { | 
|  | 150   TerminalClientStub _stub; | 
|  | 151   Application _application; | 
|  | 152   String _resolvedUrl; | 
|  | 153 | 
|  | 154   TerminalClientImpl( | 
|  | 155       this._application, this._resolvedUrl, MojoMessagePipeEndpoint endpoint) { | 
|  | 156     _stub = new TerminalClientStub.fromEndpoint(endpoint, this); | 
|  | 157   } | 
|  | 158 | 
|  | 159   @override | 
|  | 160   void connectToTerminal(files.FileProxy terminal) { | 
|  | 161     var url = Uri.parse(_resolvedUrl); | 
|  | 162     NetAddress remote_address; | 
|  | 163     try { | 
|  | 164       remote_address = _getNetAddressFromUrl(url); | 
|  | 165     } catch (e) { | 
|  | 166       fputs(terminal.ptr, 'HALP: Add a query: ?host=<host>&port=<port>\n' | 
|  | 167           '(<host> must be "localhost" or n1.n2.n3.n4)\n\n' | 
|  | 168           'Got query parameters:\n' + url.queryParameters.toString()); | 
|  | 169       ignoreFuture(terminal.close()); | 
|  | 170       return; | 
|  | 171     } | 
|  | 172 | 
|  | 173     // TODO(vtl): Currently, we only do IPv4, so this should work. | 
|  | 174     fputs(terminal.ptr, 'Connecting to: ' + | 
|  | 175         remote_address.ipv4.addr.join('.') + | 
|  | 176         ':' + | 
|  | 177         remote_address.ipv4.port.toString() + | 
|  | 178         '...'); | 
|  | 179 | 
|  | 180     var connector = new Connector(_application, terminal); | 
|  | 181     connector.connect(remote_address); | 
|  | 182   } | 
|  | 183 | 
|  | 184   // Note: May throw all sorts of things. | 
|  | 185   static NetAddress _getNetAddressFromUrl(Uri url) { | 
|  | 186     var params = url.queryParameters; | 
|  | 187     var host = params['host']; | 
|  | 188     return makeIPv4NetAddress( | 
|  | 189         (host == 'localhost') ? [127, 0, 0, 1] : Uri.parseIPv4Address(host), | 
|  | 190         int.parse(params['port'])); | 
|  | 191   } | 
|  | 192 } | 
|  | 193 | 
|  | 194 class NetcatApplication extends Application { | 
|  | 195   NetcatApplication.fromHandle(MojoHandle handle) : super.fromHandle(handle); | 
|  | 196 | 
|  | 197   @override | 
|  | 198   void acceptConnection(String requestorUrl, String resolvedUrl, | 
|  | 199       ApplicationConnection connection) { | 
|  | 200     connection.provideService(TerminalClientName, | 
|  | 201         (endpoint) => new TerminalClientImpl(this, resolvedUrl, endpoint)); | 
|  | 202   } | 
|  | 203 } | 
|  | 204 | 
|  | 205 main(List args) { | 
|  | 206   MojoHandle appHandle = new MojoHandle(args[0]); | 
|  | 207   String url = args[1]; | 
|  | 208   new NetcatApplication.fromHandle(appHandle) | 
|  | 209     ..onError = (() { | 
|  | 210       assert(MojoHandle.reportLeakedHandles()); | 
|  | 211     }); | 
|  | 212 } | 
| OLD | NEW | 
|---|