| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.io; | 5 part of dart.io; |
| 6 | 6 |
| 7 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | 7 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; |
| 8 | 8 |
| 9 class _WebSocketMessageType { | 9 class _WebSocketMessageType { |
| 10 static const int NONE = 0; | 10 static const int NONE = 0; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 */ | 43 */ |
| 44 class _WebSocketProtocolTransformer extends StreamEventTransformer { | 44 class _WebSocketProtocolTransformer extends StreamEventTransformer { |
| 45 static const int START = 0; | 45 static const int START = 0; |
| 46 static const int LEN_FIRST = 1; | 46 static const int LEN_FIRST = 1; |
| 47 static const int LEN_REST = 2; | 47 static const int LEN_REST = 2; |
| 48 static const int MASK = 3; | 48 static const int MASK = 3; |
| 49 static const int PAYLOAD = 4; | 49 static const int PAYLOAD = 4; |
| 50 static const int CLOSED = 5; | 50 static const int CLOSED = 5; |
| 51 static const int FAILURE = 6; | 51 static const int FAILURE = 6; |
| 52 | 52 |
| 53 _WebSocketProtocolTransformer() { | 53 bool _serverSide; |
| 54 |
| 55 _WebSocketProtocolTransformer([bool this._serverSide = false]) { |
| 54 _prepareForNextFrame(); | 56 _prepareForNextFrame(); |
| 55 _currentMessageType = _WebSocketMessageType.NONE; | 57 _currentMessageType = _WebSocketMessageType.NONE; |
| 56 } | 58 } |
| 57 | 59 |
| 58 /** | 60 /** |
| 59 * Process data received from the underlying communication channel. | 61 * Process data received from the underlying communication channel. |
| 60 */ | 62 */ |
| 61 void handleData(List<int> buffer, EventSink sink) { | 63 void handleData(List<int> buffer, EventSink sink) { |
| 62 int count = buffer.length; | 64 int count = buffer.length; |
| 63 int index = 0; | 65 int index = 0; |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 index++; | 218 index++; |
| 217 } | 219 } |
| 218 } catch (e) { | 220 } catch (e) { |
| 219 _state = FAILURE; | 221 _state = FAILURE; |
| 220 sink.addError(e); | 222 sink.addError(e); |
| 221 } | 223 } |
| 222 } | 224 } |
| 223 | 225 |
| 224 void _lengthDone(EventSink sink) { | 226 void _lengthDone(EventSink sink) { |
| 225 if (_masked) { | 227 if (_masked) { |
| 228 if (!_serverSide) { |
| 229 throw new WebSocketException("Received masked frame from server"); |
| 230 } |
| 226 _state = MASK; | 231 _state = MASK; |
| 227 _remainingMaskingKeyBytes = 4; | 232 _remainingMaskingKeyBytes = 4; |
| 228 } else { | 233 } else { |
| 234 if (_serverSide) { |
| 235 throw new WebSocketException("Received unmasked frame from client"); |
| 236 } |
| 229 _remainingPayloadBytes = _len; | 237 _remainingPayloadBytes = _len; |
| 230 _startPayload(sink); | 238 _startPayload(sink); |
| 231 } | 239 } |
| 232 } | 240 } |
| 233 | 241 |
| 234 void _maskDone(EventSink sink) { | 242 void _maskDone(EventSink sink) { |
| 235 _remainingPayloadBytes = _len; | 243 _remainingPayloadBytes = _len; |
| 236 _startPayload(sink); | 244 _startPayload(sink); |
| 237 } | 245 } |
| 238 | 246 |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 response.statusCode = HttpStatus.SWITCHING_PROTOCOLS; | 393 response.statusCode = HttpStatus.SWITCHING_PROTOCOLS; |
| 386 response.headers.add(HttpHeaders.CONNECTION, "Upgrade"); | 394 response.headers.add(HttpHeaders.CONNECTION, "Upgrade"); |
| 387 response.headers.add(HttpHeaders.UPGRADE, "websocket"); | 395 response.headers.add(HttpHeaders.UPGRADE, "websocket"); |
| 388 String key = request.headers.value("Sec-WebSocket-Key"); | 396 String key = request.headers.value("Sec-WebSocket-Key"); |
| 389 SHA1 sha1 = new SHA1(); | 397 SHA1 sha1 = new SHA1(); |
| 390 sha1.add("$key$_webSocketGUID".codeUnits); | 398 sha1.add("$key$_webSocketGUID".codeUnits); |
| 391 String accept = _Base64._encode(sha1.close()); | 399 String accept = _Base64._encode(sha1.close()); |
| 392 response.headers.add("Sec-WebSocket-Accept", accept); | 400 response.headers.add("Sec-WebSocket-Accept", accept); |
| 393 response.headers.contentLength = 0; | 401 response.headers.contentLength = 0; |
| 394 return response.detachSocket() | 402 return response.detachSocket() |
| 395 .then((socket) => new _WebSocketImpl._fromSocket(socket)); | 403 .then((socket) => new _WebSocketImpl._fromSocket(socket, true)); |
| 396 } | 404 } |
| 397 | 405 |
| 398 static bool _isUpgradeRequest(HttpRequest request) { | 406 static bool _isUpgradeRequest(HttpRequest request) { |
| 399 if (request.method != "GET") { | 407 if (request.method != "GET") { |
| 400 return false; | 408 return false; |
| 401 } | 409 } |
| 402 if (request.headers[HttpHeaders.CONNECTION] == null) { | 410 if (request.headers[HttpHeaders.CONNECTION] == null) { |
| 403 return false; | 411 return false; |
| 404 } | 412 } |
| 405 bool isUpgrade = false; | 413 bool isUpgrade = false; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 421 } | 429 } |
| 422 return true; | 430 return true; |
| 423 } | 431 } |
| 424 } | 432 } |
| 425 | 433 |
| 426 | 434 |
| 427 class _WebSocketImpl extends Stream implements WebSocket { | 435 class _WebSocketImpl extends Stream implements WebSocket { |
| 428 final StreamController _controller = new StreamController(); | 436 final StreamController _controller = new StreamController(); |
| 429 | 437 |
| 430 final Socket _socket; | 438 final Socket _socket; |
| 439 final bool _serverSide; |
| 431 int _readyState = WebSocket.CONNECTING; | 440 int _readyState = WebSocket.CONNECTING; |
| 432 bool _writeClosed = false; | 441 bool _writeClosed = false; |
| 433 int _closeCode; | 442 int _closeCode; |
| 434 String _closeReason; | 443 String _closeReason; |
| 435 | 444 |
| 436 static final HttpClient _httpClient = new HttpClient(); | 445 static final HttpClient _httpClient = new HttpClient(); |
| 437 | 446 |
| 438 static Future<WebSocket> connect(String url, [protocols]) { | 447 static Future<WebSocket> connect(String url, [protocols]) { |
| 439 Uri uri = Uri.parse(url); | 448 Uri uri = Uri.parse(url); |
| 440 if (uri.scheme != "ws" && uri.scheme != "wss") { | 449 if (uri.scheme != "ws" && uri.scheme != "wss") { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 498 for (int i = 0; i < expectedAccept.length; i++) { | 507 for (int i = 0; i < expectedAccept.length; i++) { |
| 499 if (expectedAccept[i] != receivedAccept[i]) { | 508 if (expectedAccept[i] != receivedAccept[i]) { |
| 500 error("Bad response 'Sec-WebSocket-Accept' header"); | 509 error("Bad response 'Sec-WebSocket-Accept' header"); |
| 501 } | 510 } |
| 502 } | 511 } |
| 503 return response.detachSocket() | 512 return response.detachSocket() |
| 504 .then((socket) => new _WebSocketImpl._fromSocket(socket)); | 513 .then((socket) => new _WebSocketImpl._fromSocket(socket)); |
| 505 }); | 514 }); |
| 506 } | 515 } |
| 507 | 516 |
| 508 _WebSocketImpl._fromSocket(Socket this._socket) { | 517 _WebSocketImpl._fromSocket(Socket this._socket, |
| 518 [bool this._serverSide = false]) { |
| 509 _readyState = WebSocket.OPEN; | 519 _readyState = WebSocket.OPEN; |
| 510 | 520 |
| 511 bool closed = false; | 521 bool closed = false; |
| 512 var transformer = new _WebSocketProtocolTransformer(); | 522 var transformer = new _WebSocketProtocolTransformer(_serverSide); |
| 513 _socket.transform(transformer).listen( | 523 _socket.transform(transformer).listen( |
| 514 (data) { | 524 (data) { |
| 515 _controller.add(data); | 525 _controller.add(data); |
| 516 }, | 526 }, |
| 517 onError: (error) { | 527 onError: (error) { |
| 518 if (closed) return; | 528 if (closed) return; |
| 519 closed = true; | 529 closed = true; |
| 520 _controller.addError(error); | 530 _controller.addError(error); |
| 521 _controller.close(); | 531 _controller.close(); |
| 522 }, | 532 }, |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 624 data = message; | 634 data = message; |
| 625 } | 635 } |
| 626 } else { | 636 } else { |
| 627 opcode = _WebSocketOpcode.TEXT; | 637 opcode = _WebSocketOpcode.TEXT; |
| 628 } | 638 } |
| 629 _sendFrame(opcode, data); | 639 _sendFrame(opcode, data); |
| 630 } | 640 } |
| 631 | 641 |
| 632 void _sendFrame(int opcode, [List<int> data]) { | 642 void _sendFrame(int opcode, [List<int> data]) { |
| 633 if (_writeClosed) return; | 643 if (_writeClosed) return; |
| 634 bool mask = false; // Masking not implemented for server. | 644 bool mask = !_serverSide; // Masking not implemented for server. |
| 635 int dataLength = data == null ? 0 : data.length; | 645 int dataLength = data == null ? 0 : data.length; |
| 636 // Determine the header size. | 646 // Determine the header size. |
| 637 int headerSize = (mask) ? 6 : 2; | 647 int headerSize = (mask) ? 6 : 2; |
| 638 if (dataLength > 65535) { | 648 if (dataLength > 65535) { |
| 639 headerSize += 8; | 649 headerSize += 8; |
| 640 } else if (dataLength > 125) { | 650 } else if (dataLength > 125) { |
| 641 headerSize += 2; | 651 headerSize += 2; |
| 642 } | 652 } |
| 643 List<int> header = new List<int>(headerSize); | 653 List<int> header = new List<int>(headerSize); |
| 644 int index = 0; | 654 int index = 0; |
| 645 // Set FIN and opcode. | 655 // Set FIN and opcode. |
| 646 header[index++] = 0x80 | opcode; | 656 header[index++] = 0x80 | opcode; |
| 647 // Determine size and position of length field. | 657 // Determine size and position of length field. |
| 648 int lengthBytes = 1; | 658 int lengthBytes = 1; |
| 649 int firstLengthByte = 1; | 659 int firstLengthByte = 1; |
| 650 if (dataLength > 65535) { | 660 if (dataLength > 65535) { |
| 651 header[index++] = 127; | 661 header[index++] = 127; |
| 652 lengthBytes = 8; | 662 lengthBytes = 8; |
| 653 } else if (dataLength > 125) { | 663 } else if (dataLength > 125) { |
| 654 header[index++] = 126; | 664 header[index++] = 126; |
| 655 lengthBytes = 2; | 665 lengthBytes = 2; |
| 656 } | 666 } |
| 657 // Write the length in network byte order into the header. | 667 // Write the length in network byte order into the header. |
| 658 for (int i = 0; i < lengthBytes; i++) { | 668 for (int i = 0; i < lengthBytes; i++) { |
| 659 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; | 669 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; |
| 660 } | 670 } |
| 671 if (mask) { |
| 672 header[1] |= 1 << 7; |
| 673 var maskBytes = _IOCrypto.getRandomBytes(4); |
| 674 header.setRange(index, 4, maskBytes); |
| 675 index += 4; |
| 676 if (data != null) { |
| 677 var list = new Uint8List(data.length); |
| 678 for (int i = 0; i < data.length; i++) { |
| 679 list[i] = data[i] ^ maskBytes[i % 4]; |
| 680 } |
| 681 data = list; |
| 682 } |
| 683 } |
| 661 assert(index == headerSize); | 684 assert(index == headerSize); |
| 662 try { | 685 try { |
| 663 _socket.writeBytes(header); | 686 _socket.writeBytes(header); |
| 664 if (data != null) { | 687 if (data != null) { |
| 665 _socket.writeBytes(data); | 688 _socket.writeBytes(data); |
| 666 } | 689 } |
| 667 } catch (_) { | 690 } catch (_) { |
| 668 // The socket can be closed before _socket.done have a chance | 691 // The socket can be closed before _socket.done have a chance |
| 669 // to complete. | 692 // to complete. |
| 670 _writeClosed = true; | 693 _writeClosed = true; |
| 671 } | 694 } |
| 672 } | 695 } |
| 673 } | 696 } |
| OLD | NEW |