Custom WebSocket Connection logic.
Source
static Future<WebSocket> connectToWebSocket(
String url, {
Iterable<String> protocols,
Map<String, dynamic> headers,
HttpClient httpClient,
bool useStandardWebSocket
}) async {
Uri uri = Uri.parse(url);
if (useStandardWebSocket == null) {
useStandardWebSocket = enableStandardWebSocket;
}
if (useStandardWebSocket == true && uri.scheme != "wss") {
return await awaitWithTimeout(WebSocket.connect(
url,
protocols: protocols,
headers: headers
), 60000, onSuccessAfterTimeout: (WebSocket socket){
socket.close();
});
}
if (uri.scheme != "ws" && uri.scheme != "wss") {
throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'");
}
Random random = new Random();
// Generate 16 random bytes.
Uint8List nonceData = new Uint8List(16);
for (int i = 0; i < 16; i++) {
nonceData[i] = random.nextInt(256);
}
String nonce = BASE64.encode(nonceData);
int port = uri.port;
if (port == 0) {
port = uri.scheme == "wss" ? 443 : 80;
}
uri = new Uri(
scheme: uri.scheme == "wss" ? "https" : "http",
userInfo: uri.userInfo,
host: uri.host,
port: port,
path: uri.path,
query: uri.query
);
HttpClient _client = httpClient == null ? (
new HttpClient()
..badCertificateCallback = (a, b, c) => true
) : httpClient;
return _client.openUrl("GET", uri).then((HttpClientRequest request) async {
if (uri.userInfo != null && !uri.userInfo.isEmpty) {
// If the URL contains user information use that for basic
// authorization.
String auth = BASE64.encode(UTF8.encode(uri.userInfo));
request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
}
if (headers != null) {
headers.forEach((field, value) => request.headers.add(field, value));
}
// Setup the initial handshake.
request.headers
..set(HttpHeaders.CONNECTION, "Upgrade")
..set(HttpHeaders.UPGRADE, "websocket")
..set("Sec-WebSocket-Key", nonce)
..set("Cache-Control", "no-cache")
..set("Sec-WebSocket-Version", "13");
if (protocols != null) {
request.headers.add("Sec-WebSocket-Protocol", protocols.toList());
}
return request.close();
}).then((response) {
return response;
}).then((HttpClientResponse response) {
void error(String message) {
// Flush data.
response.detachSocket().then((Socket socket) {
socket.destroy();
});
throw new WebSocketException(message);
}
if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS ||
response.headers[HttpHeaders.CONNECTION] == null ||
!response.headers[HttpHeaders.CONNECTION].any(
(value) => value.toLowerCase() == "upgrade") ||
response.headers.value(HttpHeaders.UPGRADE).toLowerCase() != "websocket") {
error("Connection to '$uri' was not upgraded to websocket");
}
String accept = response.headers.value("Sec-WebSocket-Accept");
if (accept == null) {
error("Response did not contain a 'Sec-WebSocket-Accept' header");
}
List<int> expectedAccept = sha1.convert("$nonce$_webSocketGUID".codeUnits).bytes;
List<int> receivedAccept = BASE64.decode(accept);
if (expectedAccept.length != receivedAccept.length) {
error("Response header 'Sec-WebSocket-Accept' is the wrong length");
}
for (int i = 0; i < expectedAccept.length; i++) {
if (expectedAccept[i] != receivedAccept[i]) {
error("Bad response 'Sec-WebSocket-Accept' header");
}
}
var protocol = response.headers.value('Sec-WebSocket-Protocol');
return response.detachSocket().then((socket) {
socket.setOption(SocketOption.TCP_NODELAY, _tcpNoDelay);
return new WebSocket.fromUpgradedSocket(
socket,
protocol: protocol,
serverSide: false
);
});
}).timeout(new Duration(minutes: 1), onTimeout:(){
_client.close(force: true);
throw new WebSocketException('timeout');
});
}