Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Side by Side Diff: sdk/lib/io/websocket_impl.dart

Issue 13686005: WebSocket: Correctly expect masked frames from clients, and send masked frames from the client. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http_session.dart ('k') | tests/standalone/io/web_socket_protocol_processor_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698