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 |