 Chromium Code Reviews
 Chromium Code Reviews Issue 130913010:
  Use a list of Uint8List-views instead of a BytesBuilder, to build up websocket messages.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
    
  
    Issue 130913010:
  Use a list of Uint8List-views instead of a BytesBuilder, to build up websocket messages.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart| 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 // Matches _WebSocketOpcode. | 9 // Matches _WebSocketOpcode. | 
| 10 class _WebSocketMessageType { | 10 class _WebSocketMessageType { | 
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 62 int _remainingPayloadBytes = -1; | 62 int _remainingPayloadBytes = -1; | 
| 63 int _unmaskingIndex = 0; | 63 int _unmaskingIndex = 0; | 
| 64 int _currentMessageType = _WebSocketMessageType.NONE; | 64 int _currentMessageType = _WebSocketMessageType.NONE; | 
| 65 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 65 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 
| 66 String closeReason = ""; | 66 String closeReason = ""; | 
| 67 | 67 | 
| 68 EventSink _eventSink; | 68 EventSink _eventSink; | 
| 69 | 69 | 
| 70 final bool _serverSide; | 70 final bool _serverSide; | 
| 71 final List _maskingBytes = new List(4); | 71 final List _maskingBytes = new List(4); | 
| 72 final BytesBuilder _payload = new BytesBuilder(); | 72 final List<Uint8List> _payloads = new List<Uint8List>(); | 
| 73 | 73 | 
| 74 _WebSocketProtocolTransformer([this._serverSide = false]); | 74 _WebSocketProtocolTransformer([this._serverSide = false]); | 
| 75 | 75 | 
| 76 Stream bind(Stream stream) { | 76 Stream bind(Stream stream) { | 
| 77 return new Stream.eventTransformed( | 77 return new Stream.eventTransformed( | 
| 78 stream, | 78 stream, | 
| 79 (EventSink eventSink) { | 79 (EventSink eventSink) { | 
| 80 if (_eventSink != null) { | 80 if (_eventSink != null) { | 
| 81 throw new StateError("WebSocket transformer already used."); | 81 throw new StateError("WebSocket transformer already used."); | 
| 82 } | 82 } | 
| 83 _eventSink = eventSink; | 83 _eventSink = eventSink; | 
| 84 return this; | 84 return this; | 
| 85 }); | 85 }); | 
| 86 } | 86 } | 
| 87 | 87 | 
| 88 void addError(Object error, [StackTrace stackTrace]) => | 88 void addError(Object error, [StackTrace stackTrace]) => | 
| 89 _eventSink.addError(error, stackTrace); | 89 _eventSink.addError(error, stackTrace); | 
| 90 | 90 | 
| 91 void close() => _eventSink.close(); | 91 void close() => _eventSink.close(); | 
| 92 | 92 | 
| 93 Uint8List _takePayload() { | |
| 94 if (_payloads.length == 0) return new Uint8List(0); | |
| 
kasperl
2014/01/29 11:10:44
Do you have to create a new one here? Cache an emp
 
Anders Johnsen
2014/01/29 14:23:40
The empty-list case is very rare, and not somethin
 | |
| 95 if (_payloads.length == 1) { | |
| 96 Uint8List result = _payloads.single; | |
| 97 _payloads.clear(); | |
| 98 return result; | |
| 99 } | |
| 100 int length = 0; | |
| 101 for (Uint8List payload in _payloads) { | |
| 102 length += payload.length; | |
| 103 } | |
| 104 Uint8List result = new Uint8List(length); | |
| 105 int offset = 0; | |
| 106 for (Uint8List payload in _payloads) { | |
| 107 result.setRange(offset, offset + payload.length, payload); | |
| 108 offset += payload.length; | |
| 109 } | |
| 110 _payloads.clear(); | |
| 111 return result; | |
| 112 } | |
| 113 | |
| 93 /** | 114 /** | 
| 94 * Process data received from the underlying communication channel. | 115 * Process data received from the underlying communication channel. | 
| 95 */ | 116 */ | 
| 96 void add(Uint8List buffer) { | 117 void add(Uint8List buffer) { | 
| 97 int count = buffer.length; | 118 int count = buffer.length; | 
| 98 int index = 0; | 119 int index = 0; | 
| 99 int lastIndex = count; | 120 int lastIndex = count; | 
| 100 if (_state == CLOSED) { | 121 if (_state == CLOSED) { | 
| 101 throw new WebSocketException("Data on closed connection"); | 122 throw new WebSocketException("Data on closed connection"); | 
| 102 } | 123 } | 
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 168 } | 189 } | 
| 169 } else { | 190 } else { | 
| 170 assert(_state == PAYLOAD); | 191 assert(_state == PAYLOAD); | 
| 171 // The payload is not handled one byte at a time but in blocks. | 192 // The payload is not handled one byte at a time but in blocks. | 
| 172 int payloadLength = min(lastIndex - index, _remainingPayloadBytes); | 193 int payloadLength = min(lastIndex - index, _remainingPayloadBytes); | 
| 173 _remainingPayloadBytes -= payloadLength; | 194 _remainingPayloadBytes -= payloadLength; | 
| 174 // Unmask payload if masked. | 195 // Unmask payload if masked. | 
| 175 if (_masked) { | 196 if (_masked) { | 
| 176 _unmask(index, payloadLength, buffer); | 197 _unmask(index, payloadLength, buffer); | 
| 177 } | 198 } | 
| 178 // Control frame and data frame share _payload builder. | 199 // Control frame and data frame share _payloads. | 
| 179 _payload.add(new Uint8List.view(buffer.buffer, index, payloadLength)); | 200 _payloads.add( | 
| 201 new Uint8List.view(buffer.buffer, index, payloadLength)); | |
| 180 index += payloadLength; | 202 index += payloadLength; | 
| 181 if (_isControlFrame()) { | 203 if (_isControlFrame()) { | 
| 182 if (_remainingPayloadBytes == 0) _controlFrameEnd(); | 204 if (_remainingPayloadBytes == 0) _controlFrameEnd(); | 
| 183 } else { | 205 } else { | 
| 184 if (_currentMessageType != _WebSocketMessageType.TEXT && | 206 if (_currentMessageType != _WebSocketMessageType.TEXT && | 
| 185 _currentMessageType != _WebSocketMessageType.BINARY) { | 207 _currentMessageType != _WebSocketMessageType.BINARY) { | 
| 186 throw new WebSocketException("Protocol error"); | 208 throw new WebSocketException("Protocol error"); | 
| 187 } | 209 } | 
| 188 if (_remainingPayloadBytes == 0) _messageFrameEnd(); | 210 if (_remainingPayloadBytes == 0) _messageFrameEnd(); | 
| 189 } | 211 } | 
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 278 } | 300 } | 
| 279 } else { | 301 } else { | 
| 280 _state = PAYLOAD; | 302 _state = PAYLOAD; | 
| 281 } | 303 } | 
| 282 } | 304 } | 
| 283 | 305 | 
| 284 void _messageFrameEnd() { | 306 void _messageFrameEnd() { | 
| 285 if (_fin) { | 307 if (_fin) { | 
| 286 switch (_currentMessageType) { | 308 switch (_currentMessageType) { | 
| 287 case _WebSocketMessageType.TEXT: | 309 case _WebSocketMessageType.TEXT: | 
| 288 _eventSink.add(UTF8.decode(_payload.takeBytes())); | 310 _eventSink.add(UTF8.decode(_takePayload())); | 
| 289 break; | 311 break; | 
| 290 case _WebSocketMessageType.BINARY: | 312 case _WebSocketMessageType.BINARY: | 
| 291 _eventSink.add(_payload.takeBytes()); | 313 _eventSink.add(_takePayload()); | 
| 292 break; | 314 break; | 
| 293 } | 315 } | 
| 294 _currentMessageType = _WebSocketMessageType.NONE; | 316 _currentMessageType = _WebSocketMessageType.NONE; | 
| 295 } | 317 } | 
| 296 _prepareForNextFrame(); | 318 _prepareForNextFrame(); | 
| 297 } | 319 } | 
| 298 | 320 | 
| 299 void _controlFrameEnd() { | 321 void _controlFrameEnd() { | 
| 300 switch (_opcode) { | 322 switch (_opcode) { | 
| 301 case _WebSocketOpcode.CLOSE: | 323 case _WebSocketOpcode.CLOSE: | 
| 302 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 324 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 
| 303 if (_payload.length > 0) { | 325 var payload = _takePayload(); | 
| 304 var bytes = _payload.takeBytes(); | 326 if (payload.length > 0) { | 
| 305 if (bytes.length == 1) { | 327 if (payload.length == 1) { | 
| 306 throw new WebSocketException("Protocol error"); | 328 throw new WebSocketException("Protocol error"); | 
| 307 } | 329 } | 
| 308 closeCode = bytes[0] << 8 | bytes[1]; | 330 closeCode = payload[0] << 8 | payload[1]; | 
| 309 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { | 331 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { | 
| 310 throw new WebSocketException("Protocol error"); | 332 throw new WebSocketException("Protocol error"); | 
| 311 } | 333 } | 
| 312 if (bytes.length > 2) { | 334 if (payload.length > 2) { | 
| 313 closeReason = UTF8.decode(bytes.sublist(2)); | 335 closeReason = UTF8.decode(payload.sublist(2)); | 
| 314 } | 336 } | 
| 315 } | 337 } | 
| 316 _state = CLOSED; | 338 _state = CLOSED; | 
| 317 _eventSink.close(); | 339 _eventSink.close(); | 
| 318 break; | 340 break; | 
| 319 | 341 | 
| 320 case _WebSocketOpcode.PING: | 342 case _WebSocketOpcode.PING: | 
| 321 _eventSink.add(new _WebSocketPing(_payload.takeBytes())); | 343 _eventSink.add(new _WebSocketPing(_takePayload())); | 
| 322 break; | 344 break; | 
| 323 | 345 | 
| 324 case _WebSocketOpcode.PONG: | 346 case _WebSocketOpcode.PONG: | 
| 325 _eventSink.add(new _WebSocketPong(_payload.takeBytes())); | 347 _eventSink.add(new _WebSocketPong(_takePayload())); | 
| 326 break; | 348 break; | 
| 327 } | 349 } | 
| 328 _prepareForNextFrame(); | 350 _prepareForNextFrame(); | 
| 329 } | 351 } | 
| 330 | 352 | 
| 331 bool _isControlFrame() { | 353 bool _isControlFrame() { | 
| 332 return _opcode == _WebSocketOpcode.CLOSE || | 354 return _opcode == _WebSocketOpcode.CLOSE || | 
| 333 _opcode == _WebSocketOpcode.PING || | 355 _opcode == _WebSocketOpcode.PING || | 
| 334 _opcode == _WebSocketOpcode.PONG; | 356 _opcode == _WebSocketOpcode.PONG; | 
| 335 } | 357 } | 
| (...skipping 616 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 952 (code < WebSocketStatus.NORMAL_CLOSURE || | 974 (code < WebSocketStatus.NORMAL_CLOSURE || | 
| 953 code == WebSocketStatus.RESERVED_1004 || | 975 code == WebSocketStatus.RESERVED_1004 || | 
| 954 code == WebSocketStatus.NO_STATUS_RECEIVED || | 976 code == WebSocketStatus.NO_STATUS_RECEIVED || | 
| 955 code == WebSocketStatus.ABNORMAL_CLOSURE || | 977 code == WebSocketStatus.ABNORMAL_CLOSURE || | 
| 956 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && | 978 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && | 
| 957 code < WebSocketStatus.RESERVED_1015) || | 979 code < WebSocketStatus.RESERVED_1015) || | 
| 958 (code >= WebSocketStatus.RESERVED_1015 && | 980 (code >= WebSocketStatus.RESERVED_1015 && | 
| 959 code < 3000)); | 981 code < 3000)); | 
| 960 } | 982 } | 
| 961 } | 983 } | 
| OLD | NEW |