Chromium Code Reviews| Index: runtime/bin/sync_socket_patch.dart |
| diff --git a/runtime/bin/sync_socket_patch.dart b/runtime/bin/sync_socket_patch.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..5069ad10cc127f5df41d9fdd60dbe7bd4839590c |
| --- /dev/null |
| +++ b/runtime/bin/sync_socket_patch.dart |
| @@ -0,0 +1,324 @@ |
| +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +@patch |
| +class RawSynchronousSocket { |
| + @patch |
| + static RawSynchronousSocket connectSync(host, int port) { |
| + return _RawSynchronousSocket.connectSync(host, port); |
| + } |
| +} |
| + |
| +class _RawSynchronousSocket implements RawSynchronousSocket { |
| + final _NativeSynchronousSocket _socket; |
| + |
| + _RawSynchronousSocket(this._socket) {} |
| + |
| + static RawSynchronousSocket connectSync(host, int port) { |
| + _throwOnBadPort(port); |
| + return new _RawSynchronousSocket( |
| + _NativeSynchronousSocket.connectSync(host, port)); |
| + } |
| + |
| + InternetAddress get address => _socket.address; |
| + int get port => _socket.port; |
| + InternetAddress get remoteAddress => _socket.remoteAddress; |
| + int get remotePort => _socket.remotePort; |
| + |
| + int available() => _socket.available; |
| + |
| + void closeSync() => _socket.closeSync(); |
| + |
| + int readIntoSync(List<int> buffer, [int start = 0, int end]) => |
| + _socket.readIntoSync(buffer, start, end); |
| + |
| + List<int> readSync(int bytes) => _socket.readSync(bytes); |
| + |
| + void shutdown(SocketDirection direction) => _socket.shutdown(direction); |
| + |
| + void writeFromSync(List<int> buffer, [int start = 0, int end]) => |
| + _socket.writeFromSync(buffer, start, end); |
| +} |
| + |
| +// The NativeFieldWrapperClass1 can not be used with a mixin, due to missing |
| +// implicit constructor. |
| +class _NativeSynchronousSocketNativeWrapper extends NativeFieldWrapperClass1 {} |
| + |
| +// The _NativeSynchronousSocket class encapsulates a synchronous OS socket. |
| +class _NativeSynchronousSocket extends _NativeSynchronousSocketNativeWrapper { |
| + // Socket close state. |
| + bool isClosed = false; |
| + bool isClosedRead = false; |
| + bool isClosedWrite = false; |
| + |
| + // Holds the address used to connect the socket. |
| + InternetAddress localAddress; |
| + |
| + // Holds the port of the socket, 0 if not known. |
| + int localPort = 0; |
| + |
| + _ReadWriteResourceInfo resourceInfo; |
|
zra
2017/04/07 16:29:41
It looks like you need to call _SocketResourceInfo
bkonyi
2017/04/10 19:20:06
Added SocketClosed() call in CloseSync()
|
| + |
| + InternetAddress get address => localAddress; |
| + int get available => nativeAvailable(); |
| + |
| + int get port { |
| + if (localPort != 0) return localPort; |
|
zra
2017/04/07 16:29:41
Please put single line ifs in {}
bkonyi
2017/04/10 19:20:06
Done.
|
| + if (isClosed) throw const SocketException.closed(); |
| + var result = nativeGetPort(); |
| + if (result is OSError) throw result; |
| + return localPort = result; |
| + } |
| + |
| + InternetAddress get remoteAddress { |
| + if (isClosed) throw const SocketException.closed(); |
| + var result = nativeGetRemotePeer(); |
| + if (result is OSError) throw result; |
| + var addr = result[0]; |
| + var type = new InternetAddressType._from(addr[0]); |
| + return new _InternetAddress(addr[1], null, addr[2]); |
| + } |
| + |
| + int get remotePort { |
| + if (isClosed) throw const SocketException.closed(); |
| + var result = nativeGetRemotePeer(); |
| + if (result is OSError) throw result; |
| + return result[1]; |
| + } |
| + |
| + static _NativeSynchronousSocket connectSync(host, int port) { |
|
zra
2017/04/07 16:29:41
statics like this that generate an instance I thin
bkonyi
2017/04/10 19:20:06
Ah, I wasn't sure where static methods like this s
|
| + List<_InternetAddress> addresses = null; |
| + var error = null; |
| + if (host is _InternetAddress) { |
| + addresses = [host]; |
| + } else { |
| + try { |
| + addresses = lookup(host); |
| + } catch (e) { |
| + error = e; |
| + } |
| + if (error != null || addresses == null || addresses.isEmpty) { |
| + throw createError(error, "Failed host lookup: '$host'"); |
| + } |
| + } |
| + assert(addresses is List); |
| + var it = addresses.iterator; |
| + _NativeSynchronousSocket connectNext() { |
| + if (!it.moveNext()) { |
| + // Could not connect. Throw the first connection error we encountered. |
| + assert(error != null); |
| + throw error; |
| + } |
| + var address = it.current; |
| + var socket = new _NativeSynchronousSocket(); |
| + socket.localAddress = address; |
| + var result = socket.nativeCreateConnectSync(address._in_addr, port); |
| + if (result is OSError) { |
| + // Keep first error, if present. |
| + if (error == null) { |
| + int errorCode = result.errorCode; |
|
zra
2017/04/07 16:29:40
errorCode doesn't appear to be used.
bkonyi
2017/04/10 19:20:06
Removed.
|
| + error = createError(result, "Connection failed", address, port); |
| + } |
| + return connectNext(); |
| + } else { |
| + // Query the local port, for error messages. |
| + try { |
| + socket.port; |
| + } catch (e) { |
| + error = createError(e, "Connection failed", address, port); |
|
zra
2017/04/07 16:29:41
if (error == null)
bkonyi
2017/04/10 19:20:05
Done.
|
| + return connectNext(); |
| + } |
| + setupResourceInfo(socket); |
| + } |
| + return socket; |
| + } |
| + |
| + return connectNext(); |
| + } |
| + |
| + void closeSync() { |
| + if (!isClosed) { |
| + nativeCloseSync(); |
| + isClosed = true; |
| + } |
| + } |
| + |
| + // Create the appropriate error/exception from different returned |
| + // error objects. |
| + static createError(error, String message, |
| + [InternetAddress address, int port]) { |
| + if (error is OSError) { |
| + return new SocketException(message, |
| + osError: error, address: address, port: port); |
| + } else { |
| + return new SocketException(message, address: address, port: port); |
| + } |
| + } |
| + |
| + static List<InternetAddress> lookup(String host, |
| + {InternetAddressType type: InternetAddressType.ANY}) { |
| + var response = nativeLookupRequest(host, type._value); |
| + if (response is OSError) { |
| + throw response; |
| + } |
| + // TODO(bkonyi) do we need this first empty element? Or is this something specific |
|
zra
2017/04/07 16:29:41
I don't see what this is used for either. Let's ge
bkonyi
2017/04/10 19:20:06
Done.
|
| + // for io_service? |
| + return response.skip(1).map((result) { |
| + var type = new InternetAddressType._from(result[0]); |
| + return new _InternetAddress(result[1], host, result[2]); |
| + }).toList(); |
| + } |
| + |
| + int readIntoSync(List<int> buffer, int start, int end) { |
|
zra
2017/04/07 16:29:40
This shouldn't be implemented on top of readSync.
bkonyi
2017/04/10 19:20:06
Done.
|
| + if (buffer == null) { |
| + throw new ArgumentError("Illegal buffer: buffer cannot be null"); |
| + } |
| + if (start != null && ((start >= buffer.length) || (start < 0))) { |
| + throw new ArgumentError("Illegal start $start"); |
| + } |
| + if ((start != null && end != null) && |
| + ((start > end) || (end > buffer.length))) { |
| + throw new ArgumentError("Illegal range [$start, $end)"); |
| + } |
| + int len = 0; |
| + if (start == null) { |
| + start = 0; |
| + len = buffer.length; |
| + } else if (end == null) { |
| + len = buffer.length - start; |
| + } else { |
| + len = end - start; |
| + } |
| + List<int> result = readSync(len); |
| + len = min(result.length, len); |
| + |
| + for (int i = start; i < start + len; i++) { |
| + buffer[i] = result[i - start]; |
| + } |
| + return result.length; |
| + } |
| + |
| + List<int> readSync(int len) { |
| + if (len != null && len <= 0) { |
| + throw new ArgumentError("Illegal length $len"); |
| + } |
| + if (isClosed) return null; |
| + if (len == 0) return null; |
| + var result = nativeRead(len); |
| + if (result is OSError) { |
| + throw result; |
| + return null; |
| + } |
| + if (result != null) { |
| + assert(resourceInfo != null); |
| + if (resourceInfo != null) { |
| + resourceInfo.totalRead += result.length; |
| + } |
| + } |
| + assert(resourceInfo != null); |
|
zra
2017/04/07 16:29:41
If you move this assert above if (result != null)
bkonyi
2017/04/10 19:20:06
Done.
|
| + if (resourceInfo != null) { |
| + resourceInfo.didRead(); |
| + } |
| + return result; |
| + } |
| + |
| + static void setupResourceInfo(_NativeSynchronousSocket socket) { |
| + socket.resourceInfo = new _SocketResourceInfo(socket); |
| + } |
| + |
| + void shutdown(SocketDirection direction) { |
| + if (!isClosed) { |
|
zra
2017/04/07 16:29:41
if (isClosed) return
then un-indent, here and bel
bkonyi
2017/04/10 19:20:06
Done.
|
| + switch (direction) { |
| + case SocketDirection.RECEIVE: |
| + shutdownRead(); |
| + break; |
| + case SocketDirection.SEND: |
| + shutdownWrite(); |
| + break; |
| + case SocketDirection.BOTH: |
| + closeSync(); |
| + break; |
| + default: |
| + throw new ArgumentError(direction); |
| + } |
| + } |
| + } |
| + |
| + void shutdownRead() { |
| + if (!isClosed) { |
| + if (isClosedWrite) { |
| + closeSync(); |
| + } else { |
| + nativeShutdownRead(); |
|
zra
2017/04/07 16:29:41
What if isClosedRead is already true?
bkonyi
2017/04/10 19:20:05
Then the native shutdown read method would be call
|
| + } |
| + isClosedRead = true; |
| + } |
| + } |
| + |
| + void shutdownWrite() { |
| + if (!isClosed) { |
| + if (isClosedRead) { |
| + closeSync(); |
| + } else { |
| + nativeShutdownWrite(); |
| + } |
| + isClosedWrite = true; |
| + } |
| + } |
| + |
| + void writeFromSync(List<int> buffer, int start, int end) { |
|
zra
2017/04/07 16:29:41
Have a look at _RandomAccessFile.writeFromSync for
bkonyi
2017/04/10 19:20:05
Done.
|
| + if (isClosed) return; |
| + int offset = start; |
| + int bytes = null; |
| + if (buffer is! List) throw new ArgumentError(); |
|
zra
2017/04/07 16:29:41
Please give an error message.
bkonyi
2017/04/10 19:20:06
Acknowledged.
|
| + if (offset == null) offset = 0; |
| + if (end == null) { |
|
zra
2017/04/07 16:29:41
Can you use RangeError.checkValidRange()?
bkonyi
2017/04/10 19:20:06
I didn't know that existed... super useful! Done.
|
| + if (offset > (buffer.length)) { |
| + throw new RangeError.value(offset); |
| + } |
| + bytes = buffer.length - offset; |
| + } else { |
| + bytes = end - offset; |
| + } |
| + if (offset is! int || bytes is! int) { |
|
zra
2017/04/07 16:29:40
This should probably be up at the top of the funct
bkonyi
2017/04/10 19:20:06
Acknowledged.
|
| + throw new ArgumentError("Invalid arguments to write on Socket"); |
| + } |
| + if (bytes == 0) return; |
| + if (offset < 0) throw new RangeError.value(offset); |
| + if (bytes < 0) throw new RangeError.value(bytes); |
| + if ((offset + bytes) > buffer.length) { |
| + throw new RangeError.value(offset + bytes); |
| + } |
| + _BufferAndStart bufferAndStart = |
| + _ensureFastAndSerializableByteData(buffer, offset, offset + bytes); |
| + var result = |
| + nativeWrite(bufferAndStart.buffer, bufferAndStart.start, bytes); |
| + if (result is OSError) { |
| + throw result; |
| + } |
| + // TODO(bkonyi) we don't currently handle short writes or reads. |
| + // The result may be negative, if we forced a short write for testing |
| + // purposes. Negate the result to get the actual number of bytes written. |
| + if (result < 0) result = -result; |
|
zra
2017/04/07 16:29:41
Let's remove this logic if it isn't supported.
bkonyi
2017/04/10 19:20:05
Done.
|
| + assert(resourceInfo != null); |
| + if (resourceInfo != null) { |
| + resourceInfo.addWrite(result); |
| + } |
| + } |
| + |
| + // Native method declarations. |
| + static nativeLookupRequest(host, int type) |
| + native "SynchronousSocket_LookupRequest"; |
| + nativeCreateConnectSync(host, int port) |
| + native "SynchronousSocket_CreateConnectSync"; |
| + nativeAvailable() native "SynchronousSocket_Available"; |
| + nativeCloseSync() native "SynchronousSocket_CloseSync"; |
| + int nativeGetPort() native "SynchronousSocket_GetPort"; |
| + List nativeGetRemotePeer() native "SynchronousSocket_GetRemotePeer"; |
| + nativeRead(int len) native "SynchronousSocket_Read"; |
| + nativeShutdownRead() native "SynchronousSocket_ShutdownRead"; |
| + nativeShutdownWrite() native "SynchronousSocket_ShutdownWrite"; |
| + nativeWrite(List<int> buffer, int offset, int bytes) |
| + native "SynchronousSocket_WriteList"; |
| +} |