blob: 1f525c1b06f4a889c474934a9f5d2e01a1c7e24b [file] [log] [blame]
// 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.
// part of "common_patch.dart";
@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;
static _NativeSynchronousSocket connectSync(host, int port) {
if (host == null) {
throw new ArgumentError("Parameter host cannot be null");
}
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) {
error = createError(result, "Connection failed", address, port);
}
return connectNext();
} else {
// Query the local port, for error messages.
try {
socket.port;
} catch (e) {
if (error == null) {
error = createError(e, "Connection failed", address, port);
}
return connectNext();
}
setupResourceInfo(socket);
}
return socket;
}
return connectNext();
}
InternetAddress get address => localAddress;
int get available => nativeAvailable();
int get port {
if (localPort != 0) {
return localPort;
}
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];
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];
}
void closeSync() {
if (!isClosed) {
nativeCloseSync();
_SocketResourceInfo.SocketClosed(resourceInfo);
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;
}
List<_InternetAddress> addresses =
new List<_InternetAddress>(response.length);
for (int i = 0; i < response.length; ++i) {
var result = response[i];
addresses[i] = new _InternetAddress(result[1], host, result[2]);
}
return addresses;
}
int readIntoSync(List<int> buffer, int start, int end) {
_checkAvailable();
if (isClosedRead) {
throw new SocketException("Socket is closed for reading");
}
if ((buffer is! List) ||
((start != null) && (start is! int)) ||
((end != null) && (end is! int))) {
throw new ArgumentError("Invalid arguments to readIntoSync");
}
if (start == null) {
throw new ArgumentError("start cannot be null");
}
end = RangeError.checkValidRange(start, end, buffer.length);
if (end == start) {
return 0;
}
var result = nativeReadInto(buffer, start, (end - start));
if (result is OSError) {
throw new SocketException("readIntoSync failed", osError: result);
}
resourceInfo.addRead(result);
return result;
}
List<int> readSync(int len) {
_checkAvailable();
if (isClosedRead) {
throw new SocketException("Socket is closed for reading");
}
if ((len != null) && (len < 0)) {
throw new ArgumentError("Illegal length $len");
}
if (len == 0) {
return null;
}
var result = nativeRead(len);
if (result is OSError) {
throw result;
}
assert(resourceInfo != null);
if (result != null) {
if (resourceInfo != null) {
resourceInfo.totalRead += result.length;
}
}
if (resourceInfo != null) {
resourceInfo.didRead();
}
return result;
}
static void setupResourceInfo(_NativeSynchronousSocket socket) {
socket.resourceInfo = new _SocketResourceInfo(socket);
}
void shutdown(SocketDirection direction) {
if (isClosed) {
return;
}
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 || isClosedRead) {
return;
}
if (isClosedWrite) {
closeSync();
} else {
nativeShutdownRead();
}
isClosedRead = true;
}
void shutdownWrite() {
if (isClosed || isClosedWrite) {
return;
}
if (isClosedRead) {
closeSync();
} else {
nativeShutdownWrite();
}
isClosedWrite = true;
}
void writeFromSync(List<int> buffer, int start, int end) {
_checkAvailable();
if (isClosedWrite) {
throw new SocketException("Socket is closed for writing");
}
if ((buffer is! List) ||
((start != null) && (start is! int)) ||
((end != null) && (end is! int))) {
throw new ArgumentError("Invalid arguments to writeFromSync");
}
if (start == null) {
throw new ArgumentError("start cannot be equal to null");
}
end = RangeError.checkValidRange(start, end, buffer.length);
if (end == start) {
return;
}
_BufferAndStart bufferAndStart =
_ensureFastAndSerializableByteData(buffer, start, end);
var result = nativeWrite(bufferAndStart.buffer, bufferAndStart.start,
end - (start - bufferAndStart.start));
if (result is OSError) {
throw new SocketException("writeFromSync failed", osError: result);
}
assert(resourceInfo != null);
if (resourceInfo != null) {
resourceInfo.addWrite(result);
}
}
void _checkAvailable() {
if (isClosed) {
throw const SocketException.closed();
}
}
// 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";
nativeReadInto(List<int> buffer, int offset, int bytes)
native "SynchronousSocket_ReadList";
nativeShutdownRead() native "SynchronousSocket_ShutdownRead";
nativeShutdownWrite() native "SynchronousSocket_ShutdownWrite";
nativeWrite(List<int> buffer, int offset, int bytes)
native "SynchronousSocket_WriteList";
}