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