| 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 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 throw new WebSocketException("Data on closed connection"); | 69 throw new WebSocketException("Data on closed connection"); |
| 70 } | 70 } |
| 71 if (_state == FAILURE) { | 71 if (_state == FAILURE) { |
| 72 throw new WebSocketException("Data on failed connection"); | 72 throw new WebSocketException("Data on failed connection"); |
| 73 } | 73 } |
| 74 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { | 74 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { |
| 75 int byte = buffer[index]; | 75 int byte = buffer[index]; |
| 76 switch (_state) { | 76 switch (_state) { |
| 77 case START: | 77 case START: |
| 78 _fin = (byte & 0x80) != 0; | 78 _fin = (byte & 0x80) != 0; |
| 79 if ((byte & 0x70) != 0) { |
| 80 // The RSV1, RSV2 bits RSV3 most be all zero. |
| 81 throw new WebSocketException("Protocol error"); |
| 82 } |
| 79 _opcode = (byte & 0xF); | 83 _opcode = (byte & 0xF); |
| 80 switch (_opcode) { | 84 switch (_opcode) { |
| 81 case _WebSocketOpcode.CONTINUATION: | 85 case _WebSocketOpcode.CONTINUATION: |
| 82 if (_currentMessageType == _WebSocketMessageType.NONE) { | 86 if (_currentMessageType == _WebSocketMessageType.NONE) { |
| 83 throw new WebSocketException("Protocol error"); | 87 throw new WebSocketException("Protocol error"); |
| 84 } | 88 } |
| 85 break; | 89 break; |
| 86 | 90 |
| 87 case _WebSocketOpcode.TEXT: | 91 case _WebSocketOpcode.TEXT: |
| 88 if (_currentMessageType != _WebSocketMessageType.NONE) { | 92 if (_currentMessageType != _WebSocketMessageType.NONE) { |
| 89 throw new WebSocketException("Protocol error"); | 93 throw new WebSocketException("Protocol error"); |
| 90 } | 94 } |
| 91 _currentMessageType = _WebSocketMessageType.TEXT; | 95 _currentMessageType = _WebSocketMessageType.TEXT; |
| 92 _buffer = new StringBuffer(); | 96 _controller = new StreamController(); |
| 97 _controller.stream |
| 98 .transform(new Utf8DecoderTransformer(null)) |
| 99 .fold(new StringBuffer(), (buffer, str) => buffer..write(str)) |
| 100 .then((buffer) { |
| 101 sink.add(buffer.toString()); |
| 102 }, onError: (error) { |
| 103 sink.addError(error); |
| 104 }); |
| 93 break; | 105 break; |
| 94 | 106 |
| 95 case _WebSocketOpcode.BINARY: | 107 case _WebSocketOpcode.BINARY: |
| 96 if (_currentMessageType != _WebSocketMessageType.NONE) { | 108 if (_currentMessageType != _WebSocketMessageType.NONE) { |
| 97 throw new WebSocketException("Protocol error"); | 109 throw new WebSocketException("Protocol error"); |
| 98 } | 110 } |
| 99 _currentMessageType = _WebSocketMessageType.BINARY; | 111 _currentMessageType = _WebSocketMessageType.BINARY; |
| 100 _buffer = new _BufferList(); | 112 _controller = new StreamController(); |
| 113 _controller.stream |
| 114 .fold(new _BufferList(), (buffer, data) => buffer..add(data)) |
| 115 .then((buffer) { |
| 116 sink.add(buffer.readBytes()); |
| 117 }, onError: (error) { |
| 118 sink.addError(error); |
| 119 }); |
| 101 break; | 120 break; |
| 102 | 121 |
| 103 case _WebSocketOpcode.CLOSE: | 122 case _WebSocketOpcode.CLOSE: |
| 104 case _WebSocketOpcode.PING: | 123 case _WebSocketOpcode.PING: |
| 105 case _WebSocketOpcode.PONG: | 124 case _WebSocketOpcode.PONG: |
| 106 // Control frames cannot be fragmented. | 125 // Control frames cannot be fragmented. |
| 107 if (!_fin) throw new WebSocketException("Protocol error"); | 126 if (!_fin) throw new WebSocketException("Protocol error"); |
| 108 break; | 127 break; |
| 109 | 128 |
| 110 default: | 129 default: |
| 111 throw new WebSocketException("Protocol error"); | 130 throw new WebSocketException("Protocol error"); |
| 112 } | 131 } |
| 113 _state = LEN_FIRST; | 132 _state = LEN_FIRST; |
| 114 break; | 133 break; |
| 115 | 134 |
| 116 case LEN_FIRST: | 135 case LEN_FIRST: |
| 117 _masked = (byte & 0x80) != 0; | 136 _masked = (byte & 0x80) != 0; |
| 118 _len = byte & 0x7F; | 137 _len = byte & 0x7F; |
| 119 if (_isControlFrame() && _len > 126) { | 138 if (_isControlFrame() && _len > 125) { |
| 120 throw new WebSocketException("Protocol error"); | 139 throw new WebSocketException("Protocol error"); |
| 121 } | 140 } |
| 122 if (_len < 126) { | 141 if (_len < 126) { |
| 123 _lengthDone(sink); | 142 _lengthDone(sink); |
| 124 } else if (_len == 126) { | 143 } else if (_len == 126) { |
| 125 _len = 0; | 144 _len = 0; |
| 126 _remainingLenBytes = 2; | 145 _remainingLenBytes = 2; |
| 127 _state = LEN_REST; | 146 _state = LEN_REST; |
| 128 } else if (_len == 127) { | 147 } else if (_len == 127) { |
| 129 _len = 0; | 148 _len = 0; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 _controlPayload = new List<int>(); | 195 _controlPayload = new List<int>(); |
| 177 } | 196 } |
| 178 _controlPayload.addAll(buffer.sublist(index, index + payload)); | 197 _controlPayload.addAll(buffer.sublist(index, index + payload)); |
| 179 index += payload; | 198 index += payload; |
| 180 } | 199 } |
| 181 | 200 |
| 182 if (_remainingPayloadBytes == 0) { | 201 if (_remainingPayloadBytes == 0) { |
| 183 _controlFrameEnd(sink); | 202 _controlFrameEnd(sink); |
| 184 } | 203 } |
| 185 } else { | 204 } else { |
| 186 switch (_currentMessageType) { | 205 if (_currentMessageType != _WebSocketMessageType.TEXT && |
| 187 case _WebSocketMessageType.NONE: | 206 _currentMessageType != _WebSocketMessageType.BINARY) { |
| 188 throw new WebSocketException("Protocol error"); | 207 throw new WebSocketException("Protocol error"); |
| 189 | 208 } |
| 190 case _WebSocketMessageType.TEXT: | 209 _controller.add(new Uint8List.view(buffer.buffer, index, payload))
; |
| 191 _buffer.write(_decodeString( | 210 index += payload; |
| 192 buffer.sublist(index, index + payload))); | 211 if (_remainingPayloadBytes == 0) { |
| 193 index += payload; | 212 _messageFrameEnd(sink); |
| 194 if (_remainingPayloadBytes == 0) { | |
| 195 _messageFrameEnd(sink); | |
| 196 } | |
| 197 break; | |
| 198 | |
| 199 case _WebSocketMessageType.BINARY: | |
| 200 _buffer.write(buffer.sublist(index, index + payload)); | |
| 201 index += payload; | |
| 202 if (_remainingPayloadBytes == 0) { | |
| 203 _messageFrameEnd(sink); | |
| 204 } | |
| 205 break; | |
| 206 | |
| 207 default: | |
| 208 throw new WebSocketException("Protocol error"); | |
| 209 } | 213 } |
| 210 } | 214 } |
| 211 | 215 |
| 212 // Hack - as we always do index++ below. | 216 // Hack - as we always do index++ below. |
| 213 index--; | 217 index--; |
| 214 break; | 218 break; |
| 215 } | 219 } |
| 216 | 220 |
| 217 // Move to the next byte. | 221 // Move to the next byte. |
| 218 index++; | 222 index++; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 248 // If there is no actual payload perform perform callbacks without | 252 // If there is no actual payload perform perform callbacks without |
| 249 // going through the PAYLOAD state. | 253 // going through the PAYLOAD state. |
| 250 if (_remainingPayloadBytes == 0) { | 254 if (_remainingPayloadBytes == 0) { |
| 251 if (_isControlFrame()) { | 255 if (_isControlFrame()) { |
| 252 switch (_opcode) { | 256 switch (_opcode) { |
| 253 case _WebSocketOpcode.CLOSE: | 257 case _WebSocketOpcode.CLOSE: |
| 254 _state = CLOSED; | 258 _state = CLOSED; |
| 255 sink.close(); | 259 sink.close(); |
| 256 break; | 260 break; |
| 257 case _WebSocketOpcode.PING: | 261 case _WebSocketOpcode.PING: |
| 258 // TODO(ajohnsen): Handle ping. | 262 sink.add(new _WebSocketPing()); |
| 259 break; | 263 break; |
| 260 case _WebSocketOpcode.PONG: | 264 case _WebSocketOpcode.PONG: |
| 261 // TODO(ajohnsen): Handle pong. | 265 sink.add(new _WebSocketPong()); |
| 262 break; | 266 break; |
| 263 } | 267 } |
| 264 _prepareForNextFrame(); | 268 _prepareForNextFrame(); |
| 265 } else { | 269 } else { |
| 266 _messageFrameEnd(sink); | 270 _messageFrameEnd(sink); |
| 267 } | 271 } |
| 268 } else { | 272 } else { |
| 269 _state = PAYLOAD; | 273 _state = PAYLOAD; |
| 270 } | 274 } |
| 271 } | 275 } |
| 272 | 276 |
| 273 void _messageFrameEnd(EventSink sink) { | 277 void _messageFrameEnd(EventSink sink) { |
| 274 if (_fin) { | 278 if (_fin) { |
| 275 switch (_currentMessageType) { | 279 switch (_currentMessageType) { |
| 276 case _WebSocketMessageType.TEXT: | 280 case _WebSocketMessageType.TEXT: |
| 277 sink.add(_buffer.toString()); | 281 _controller.close(); |
| 278 break; | 282 break; |
| 279 case _WebSocketMessageType.BINARY: | 283 case _WebSocketMessageType.BINARY: |
| 280 sink.add(_buffer.readBytes()); | 284 _controller.close(); |
| 281 break; | 285 break; |
| 282 } | 286 } |
| 283 _buffer = null; | 287 _controller = null; |
| 284 _currentMessageType = _WebSocketMessageType.NONE; | 288 _currentMessageType = _WebSocketMessageType.NONE; |
| 285 } | 289 } |
| 286 _prepareForNextFrame(); | 290 _prepareForNextFrame(); |
| 287 } | 291 } |
| 288 | 292 |
| 289 void _controlFrameEnd(EventSink sink) { | 293 void _controlFrameEnd(EventSink sink) { |
| 290 switch (_opcode) { | 294 switch (_opcode) { |
| 291 case _WebSocketOpcode.CLOSE: | 295 case _WebSocketOpcode.CLOSE: |
| 292 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 296 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
| 293 if (_controlPayload.length > 0) { | 297 if (_controlPayload.length > 0) { |
| 294 if (_controlPayload.length == 1) { | 298 if (_controlPayload.length == 1) { |
| 295 throw new WebSocketException("Protocol error"); | 299 throw new WebSocketException("Protocol error"); |
| 296 } | 300 } |
| 297 closeCode = _controlPayload[0] << 8 | _controlPayload[1]; | 301 closeCode = _controlPayload[0] << 8 | _controlPayload[1]; |
| 298 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { | 302 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { |
| 299 throw new WebSocketException("Protocol error"); | 303 throw new WebSocketException("Protocol error"); |
| 300 } | 304 } |
| 301 if (_controlPayload.length > 2) { | 305 if (_controlPayload.length > 2) { |
| 302 closeReason = _decodeString( | 306 closeReason = _decodeUtf8Strict(_controlPayload.sublist(2)); |
| 303 _controlPayload.sublist(2)); | |
| 304 } | 307 } |
| 305 } | 308 } |
| 306 _state = CLOSED; | 309 _state = CLOSED; |
| 307 sink.close(); | 310 sink.close(); |
| 308 break; | 311 break; |
| 309 | 312 |
| 310 case _WebSocketOpcode.PING: | 313 case _WebSocketOpcode.PING: |
| 311 // TODO(ajohnsen): Handle ping. | 314 sink.add(new _WebSocketPing(_controlPayload)); |
| 312 break; | 315 break; |
| 313 | 316 |
| 314 case _WebSocketOpcode.PONG: | 317 case _WebSocketOpcode.PONG: |
| 315 // TODO(ajohnsen): Handle pong. | 318 sink.add(new _WebSocketPong(_controlPayload)); |
| 316 break; | 319 break; |
| 317 } | 320 } |
| 318 _prepareForNextFrame(); | 321 _prepareForNextFrame(); |
| 319 } | 322 } |
| 320 | 323 |
| 321 bool _isControlFrame() { | 324 bool _isControlFrame() { |
| 322 return _opcode == _WebSocketOpcode.CLOSE || | 325 return _opcode == _WebSocketOpcode.CLOSE || |
| 323 _opcode == _WebSocketOpcode.PING || | 326 _opcode == _WebSocketOpcode.PING || |
| 324 _opcode == _WebSocketOpcode.PONG; | 327 _opcode == _WebSocketOpcode.PONG; |
| 325 } | 328 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 344 int _len; | 347 int _len; |
| 345 bool _masked; | 348 bool _masked; |
| 346 int _maskingKey; | 349 int _maskingKey; |
| 347 int _remainingLenBytes; | 350 int _remainingLenBytes; |
| 348 int _remainingMaskingKeyBytes; | 351 int _remainingMaskingKeyBytes; |
| 349 int _remainingPayloadBytes; | 352 int _remainingPayloadBytes; |
| 350 int _unmaskingIndex; | 353 int _unmaskingIndex; |
| 351 | 354 |
| 352 int _currentMessageType; | 355 int _currentMessageType; |
| 353 List<int> _controlPayload; | 356 List<int> _controlPayload; |
| 354 var _buffer; // Either StringBuffer or _BufferList. | 357 StreamController _controller; |
| 355 | 358 |
| 356 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 359 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
| 357 String closeReason = ""; | 360 String closeReason = ""; |
| 358 } | 361 } |
| 359 | 362 |
| 360 | 363 |
| 364 class _WebSocketPing { |
| 365 final List<int> payload; |
| 366 _WebSocketPing([this.payload = null]); |
| 367 } |
| 368 |
| 369 |
| 370 class _WebSocketPong { |
| 371 final List<int> payload; |
| 372 _WebSocketPong([this.payload = null]); |
| 373 } |
| 374 |
| 375 |
| 361 class _WebSocketTransformerImpl implements WebSocketTransformer { | 376 class _WebSocketTransformerImpl implements WebSocketTransformer { |
| 362 final StreamController<WebSocket> _controller = | 377 final StreamController<WebSocket> _controller = |
| 363 new StreamController<WebSocket>(); | 378 new StreamController<WebSocket>(); |
| 364 | 379 |
| 365 Stream<WebSocket> bind(Stream<HttpRequest> stream) { | 380 Stream<WebSocket> bind(Stream<HttpRequest> stream) { |
| 366 stream.listen((request) { | 381 stream.listen((request) { |
| 367 _upgrade(request) | 382 _upgrade(request) |
| 368 .then((WebSocket webSocket) => _controller.add(webSocket)) | 383 .then((WebSocket webSocket) => _controller.add(webSocket)) |
| 369 .catchError((error) => _controller.addError(error)); | 384 .catchError((error) => _controller.addError(error)); |
| 370 }); | 385 }); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 427 } | 442 } |
| 428 } | 443 } |
| 429 | 444 |
| 430 | 445 |
| 431 class _WebSocketOutgoingTransformer extends StreamEventTransformer { | 446 class _WebSocketOutgoingTransformer extends StreamEventTransformer { |
| 432 final _WebSocketImpl webSocket; | 447 final _WebSocketImpl webSocket; |
| 433 | 448 |
| 434 _WebSocketOutgoingTransformer(_WebSocketImpl this.webSocket); | 449 _WebSocketOutgoingTransformer(_WebSocketImpl this.webSocket); |
| 435 | 450 |
| 436 void handleData(message, EventSink<List<int>> sink) { | 451 void handleData(message, EventSink<List<int>> sink) { |
| 452 if (message is _WebSocketPong) { |
| 453 addFrame(_WebSocketOpcode.PONG, message.payload, sink); |
| 454 return; |
| 455 } |
| 456 if (message is _WebSocketPing) { |
| 457 addFrame(_WebSocketOpcode.PONG, message.payload, sink); |
| 458 return; |
| 459 } |
| 437 List<int> data; | 460 List<int> data; |
| 438 int opcode; | 461 int opcode; |
| 439 if (message != null) { | 462 if (message != null) { |
| 440 if (message is String) { | 463 if (message is String) { |
| 441 opcode = _WebSocketOpcode.TEXT; | 464 opcode = _WebSocketOpcode.TEXT; |
| 442 data = _encodeString(message); | 465 data = _encodeString(message); |
| 443 } else { | 466 } else { |
| 444 if (message is !List<int>) { | 467 if (message is !List<int>) { |
| 445 throw new ArgumentError(message); | 468 throw new ArgumentError(message); |
| 446 } | 469 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 463 data.add(code & 0xFF); | 486 data.add(code & 0xFF); |
| 464 if (reason != null) { | 487 if (reason != null) { |
| 465 data.addAll(_encodeString(reason)); | 488 data.addAll(_encodeString(reason)); |
| 466 } | 489 } |
| 467 } | 490 } |
| 468 addFrame(_WebSocketOpcode.CLOSE, data, sink); | 491 addFrame(_WebSocketOpcode.CLOSE, data, sink); |
| 469 sink.close(); | 492 sink.close(); |
| 470 } | 493 } |
| 471 | 494 |
| 472 void addFrame(int opcode, List<int> data, EventSink<List<int>> sink) { | 495 void addFrame(int opcode, List<int> data, EventSink<List<int>> sink) { |
| 473 bool mask = !webSocket._serverSide; // Masking not implemented for server. | 496 createFrame(opcode, data, webSocket._serverSide).forEach(sink.add); |
| 497 } |
| 498 |
| 499 static Iterator createFrame(int opcode, List<int> data, bool serverSide) { |
| 500 bool mask = !serverSide; // Masking not implemented for server. |
| 474 int dataLength = data == null ? 0 : data.length; | 501 int dataLength = data == null ? 0 : data.length; |
| 475 // Determine the header size. | 502 // Determine the header size. |
| 476 int headerSize = (mask) ? 6 : 2; | 503 int headerSize = (mask) ? 6 : 2; |
| 477 if (dataLength > 65535) { | 504 if (dataLength > 65535) { |
| 478 headerSize += 8; | 505 headerSize += 8; |
| 479 } else if (dataLength > 125) { | 506 } else if (dataLength > 125) { |
| 480 headerSize += 2; | 507 headerSize += 2; |
| 481 } | 508 } |
| 482 List<int> header = new List<int>(headerSize); | 509 List<int> header = new List<int>(headerSize); |
| 483 int index = 0; | 510 int index = 0; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 504 index += 4; | 531 index += 4; |
| 505 if (data != null) { | 532 if (data != null) { |
| 506 var list = new Uint8List(data.length); | 533 var list = new Uint8List(data.length); |
| 507 for (int i = 0; i < data.length; i++) { | 534 for (int i = 0; i < data.length; i++) { |
| 508 list[i] = data[i] ^ maskBytes[i % 4]; | 535 list[i] = data[i] ^ maskBytes[i % 4]; |
| 509 } | 536 } |
| 510 data = list; | 537 data = list; |
| 511 } | 538 } |
| 512 } | 539 } |
| 513 assert(index == headerSize); | 540 assert(index == headerSize); |
| 514 sink.add(header); | 541 if (data == null) { |
| 515 if (data != null) { | 542 return [header]; |
| 516 sink.add(data); | 543 } else { |
| 544 return [header, data]; |
| 517 } | 545 } |
| 518 } | 546 } |
| 519 } | 547 } |
| 520 | 548 |
| 521 | 549 |
| 522 class _WebSocketConsumer implements StreamConsumer { | 550 class _WebSocketConsumer implements StreamConsumer { |
| 523 final _WebSocketImpl webSocket; | 551 final _WebSocketImpl webSocket; |
| 524 final Socket socket; | 552 final Socket socket; |
| 525 StreamController _controller; | 553 StreamController _controller; |
| 526 StreamSubscription _subscription; | 554 StreamSubscription _subscription; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 577 _done(); | 605 _done(); |
| 578 }, | 606 }, |
| 579 onError: (error) { | 607 onError: (error) { |
| 580 _done(error); | 608 _done(error); |
| 581 }, | 609 }, |
| 582 cancelOnError: true); | 610 cancelOnError: true); |
| 583 return _completer.future; | 611 return _completer.future; |
| 584 } | 612 } |
| 585 | 613 |
| 586 Future close() { | 614 Future close() { |
| 615 _ensureController(); |
| 587 Future closeSocket() { | 616 Future closeSocket() { |
| 588 return socket.close().then((_) => webSocket); | 617 return socket.close().then((_) => webSocket); |
| 589 } | 618 } |
| 590 if (_controller == null) return closeSocket(); | |
| 591 _controller.close(); | 619 _controller.close(); |
| 592 return _closeCompleter.future.then((_) => closeSocket()); | 620 return _closeCompleter.future.then((_) => closeSocket()); |
| 593 } | 621 } |
| 622 |
| 623 void add(data) { |
| 624 _ensureController(); |
| 625 _controller.add(data); |
| 626 } |
| 594 } | 627 } |
| 595 | 628 |
| 596 | 629 |
| 597 class _WebSocketImpl extends Stream implements WebSocket { | 630 class _WebSocketImpl extends Stream implements WebSocket { |
| 598 final StreamController _controller = new StreamController(); | 631 final StreamController _controller = new StreamController(); |
| 599 StreamSink _sink; | 632 StreamSink _sink; |
| 600 | 633 |
| 601 final Socket _socket; | 634 final Socket _socket; |
| 602 final bool _serverSide; | 635 final bool _serverSide; |
| 603 int _readyState = WebSocket.CONNECTING; | 636 int _readyState = WebSocket.CONNECTING; |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 675 error("Bad response 'Sec-WebSocket-Accept' header"); | 708 error("Bad response 'Sec-WebSocket-Accept' header"); |
| 676 } | 709 } |
| 677 } | 710 } |
| 678 return response.detachSocket() | 711 return response.detachSocket() |
| 679 .then((socket) => new _WebSocketImpl._fromSocket(socket)); | 712 .then((socket) => new _WebSocketImpl._fromSocket(socket)); |
| 680 }); | 713 }); |
| 681 } | 714 } |
| 682 | 715 |
| 683 _WebSocketImpl._fromSocket(Socket this._socket, | 716 _WebSocketImpl._fromSocket(Socket this._socket, |
| 684 [bool this._serverSide = false]) { | 717 [bool this._serverSide = false]) { |
| 685 _sink = new _StreamSinkImpl(new _WebSocketConsumer(this, _socket)); | 718 var consumer = new _WebSocketConsumer(this, _socket); |
| 719 _sink = new _StreamSinkImpl(consumer); |
| 686 _readyState = WebSocket.OPEN; | 720 _readyState = WebSocket.OPEN; |
| 687 | 721 |
| 688 var transformer = new _WebSocketProtocolTransformer(_serverSide); | 722 var transformer = new _WebSocketProtocolTransformer(_serverSide); |
| 689 _socket.transform(transformer).listen( | 723 _socket.transform(transformer).listen( |
| 690 (data) { | 724 (data) { |
| 691 _controller.add(data); | 725 if (data is _WebSocketPing) { |
| 726 consumer.add(new _WebSocketPong(data.payload)); |
| 727 } else if (data is _WebSocketPong) { |
| 728 // TODO(ajohnsen): Notify pong? |
| 729 } else { |
| 730 _controller.add(data); |
| 731 } |
| 692 }, | 732 }, |
| 693 onError: (error) { | 733 onError: (error) { |
| 734 if (error is ArgumentError) { |
| 735 close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA); |
| 736 } else { |
| 737 close(WebSocketStatus.PROTOCOL_ERROR); |
| 738 } |
| 694 _controller.addError(error); | 739 _controller.addError(error); |
| 695 _controller.close(); | 740 _controller.close(); |
| 696 }, | 741 }, |
| 697 onDone: () { | 742 onDone: () { |
| 698 if (_readyState == WebSocket.OPEN) { | 743 if (_readyState == WebSocket.OPEN) { |
| 699 _readyState = WebSocket.CLOSING; | 744 _readyState = WebSocket.CLOSING; |
| 700 if (transformer.closeCode != WebSocketStatus.NO_STATUS_RECEIVED) { | 745 if (!_isReservedStatusCode(transformer.closeCode)) { |
| 701 close(transformer.closeCode); | 746 close(transformer.closeCode); |
| 702 } else { | 747 } else { |
| 703 close(); | 748 close(); |
| 704 } | 749 } |
| 705 _readyState = WebSocket.CLOSED; | 750 _readyState = WebSocket.CLOSED; |
| 706 } | 751 } |
| 707 _closeCode = transformer.closeCode; | 752 _closeCode = transformer.closeCode; |
| 708 _closeReason = transformer.closeReason; | 753 _closeReason = transformer.closeReason; |
| 709 _controller.close(); | 754 _controller.close(); |
| 710 }, | 755 }, |
| (...skipping 17 matching lines...) Expand all Loading... |
| 728 int get closeCode => _closeCode; | 773 int get closeCode => _closeCode; |
| 729 String get closeReason => _closeReason; | 774 String get closeReason => _closeReason; |
| 730 | 775 |
| 731 void add(data) => _sink.add(data); | 776 void add(data) => _sink.add(data); |
| 732 void addError(error) => _sink.addError(error); | 777 void addError(error) => _sink.addError(error); |
| 733 Future addStream(Stream stream) => _sink.addStream(stream); | 778 Future addStream(Stream stream) => _sink.addStream(stream); |
| 734 Future get done => _sink.done; | 779 Future get done => _sink.done; |
| 735 | 780 |
| 736 Future close([int code, String reason]) { | 781 Future close([int code, String reason]) { |
| 737 if (!_writeClosed) { | 782 if (!_writeClosed) { |
| 738 if (code == WebSocketStatus.RESERVED_1004 || | 783 if (_isReservedStatusCode(code)) { |
| 739 code == WebSocketStatus.NO_STATUS_RECEIVED || | |
| 740 code == WebSocketStatus.RESERVED_1015) { | |
| 741 throw new WebSocketException("Reserved status code $code"); | 784 throw new WebSocketException("Reserved status code $code"); |
| 742 } | 785 } |
| 743 _outCloseCode = code; | 786 _outCloseCode = code; |
| 744 _outCloseReason = reason; | 787 _outCloseReason = reason; |
| 745 _writeClosed = true; | 788 _writeClosed = true; |
| 746 } | 789 } |
| 747 return _sink.close(); | 790 return _sink.close(); |
| 748 } | 791 } |
| 792 |
| 793 static bool _isReservedStatusCode(int code) { |
| 794 return code != null && |
| 795 (code < WebSocketStatus.NORMAL_CLOSURE || |
| 796 code == WebSocketStatus.RESERVED_1004 || |
| 797 code == WebSocketStatus.NO_STATUS_RECEIVED || |
| 798 code == WebSocketStatus.ABNORMAL_CLOSURE || |
| 799 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && |
| 800 code < WebSocketStatus.RESERVED_1015) || |
| 801 (code >= WebSocketStatus.RESERVED_1015 && |
| 802 code < 3000)); |
| 803 } |
| 749 } | 804 } |
| OLD | NEW |