| 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 23 matching lines...) Expand all Loading... |
| 34 | 34 |
| 35 /** | 35 /** |
| 36 * The web socket protocol transformer handles the protocol byte stream | 36 * The web socket protocol transformer handles the protocol byte stream |
| 37 * which is supplied through the [:handleData:]. As the protocol is processed, | 37 * which is supplied through the [:handleData:]. As the protocol is processed, |
| 38 * it'll output frame data as either a List<int> or String. | 38 * it'll output frame data as either a List<int> or String. |
| 39 * | 39 * |
| 40 * Important infomation about usage: Be sure you use cancelOnError, so the | 40 * Important infomation about usage: Be sure you use cancelOnError, so the |
| 41 * socket will be closed when the processer encounter an error. Not using it | 41 * socket will be closed when the processer encounter an error. Not using it |
| 42 * will lead to undefined behaviour. | 42 * will lead to undefined behaviour. |
| 43 */ | 43 */ |
| 44 class _WebSocketProtocolTransformer extends StreamEventTransformer { | 44 // TODO(ajohnsen): make this transformer reusable? |
| 45 class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
| 45 static const int START = 0; | 46 static const int START = 0; |
| 46 static const int LEN_FIRST = 1; | 47 static const int LEN_FIRST = 1; |
| 47 static const int LEN_REST = 2; | 48 static const int LEN_REST = 2; |
| 48 static const int MASK = 3; | 49 static const int MASK = 3; |
| 49 static const int PAYLOAD = 4; | 50 static const int PAYLOAD = 4; |
| 50 static const int CLOSED = 5; | 51 static const int CLOSED = 5; |
| 51 static const int FAILURE = 6; | 52 static const int FAILURE = 6; |
| 52 | 53 |
| 53 bool _serverSide; | 54 bool _serverSide; |
| 55 EventSink _eventSink; |
| 54 | 56 |
| 55 _WebSocketProtocolTransformer([bool this._serverSide = false]) { | 57 _WebSocketProtocolTransformer([bool this._serverSide = false]) { |
| 56 _prepareForNextFrame(); | 58 _prepareForNextFrame(); |
| 57 _currentMessageType = _WebSocketMessageType.NONE; | 59 _currentMessageType = _WebSocketMessageType.NONE; |
| 58 } | 60 } |
| 59 | 61 |
| 62 Stream bind(Stream stream) { |
| 63 return new Stream.eventTransformed( |
| 64 stream, |
| 65 (EventSink eventSink) { |
| 66 if (_eventSink != null) { |
| 67 throw new StateError("WebSocket transformer already used."); |
| 68 } |
| 69 _eventSink = eventSink; |
| 70 return this; |
| 71 }); |
| 72 } |
| 73 |
| 74 void addError(Object error, [StackTrace stackTrace]) { |
| 75 _eventSink.addError(error, stackTrace); |
| 76 } |
| 77 |
| 78 void close() => _eventSink.close(); |
| 79 |
| 60 /** | 80 /** |
| 61 * Process data received from the underlying communication channel. | 81 * Process data received from the underlying communication channel. |
| 62 */ | 82 */ |
| 63 void handleData(Uint8List buffer, EventSink sink) { | 83 void add(Uint8List buffer) { |
| 64 int count = buffer.length; | 84 int count = buffer.length; |
| 65 int index = 0; | 85 int index = 0; |
| 66 int lastIndex = count; | 86 int lastIndex = count; |
| 67 try { | 87 try { |
| 68 if (_state == CLOSED) { | 88 if (_state == CLOSED) { |
| 69 throw new WebSocketException("Data on closed connection"); | 89 throw new WebSocketException("Data on closed connection"); |
| 70 } | 90 } |
| 71 if (_state == FAILURE) { | 91 if (_state == FAILURE) { |
| 72 throw new WebSocketException("Data on failed connection"); | 92 throw new WebSocketException("Data on failed connection"); |
| 73 } | 93 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 91 case _WebSocketOpcode.TEXT: | 111 case _WebSocketOpcode.TEXT: |
| 92 if (_currentMessageType != _WebSocketMessageType.NONE) { | 112 if (_currentMessageType != _WebSocketMessageType.NONE) { |
| 93 throw new WebSocketException("Protocol error"); | 113 throw new WebSocketException("Protocol error"); |
| 94 } | 114 } |
| 95 _currentMessageType = _WebSocketMessageType.TEXT; | 115 _currentMessageType = _WebSocketMessageType.TEXT; |
| 96 _controller = new StreamController(sync: true); | 116 _controller = new StreamController(sync: true); |
| 97 _controller.stream | 117 _controller.stream |
| 98 .transform(UTF8.decoder) | 118 .transform(UTF8.decoder) |
| 99 .fold(new StringBuffer(), (buffer, str) => buffer..write(str)) | 119 .fold(new StringBuffer(), (buffer, str) => buffer..write(str)) |
| 100 .then((buffer) { | 120 .then((buffer) { |
| 101 sink.add(buffer.toString()); | 121 _eventSink.add(buffer.toString()); |
| 102 }, onError: sink.addError); | 122 }, onError: _eventSink.addError); |
| 103 break; | 123 break; |
| 104 | 124 |
| 105 case _WebSocketOpcode.BINARY: | 125 case _WebSocketOpcode.BINARY: |
| 106 if (_currentMessageType != _WebSocketMessageType.NONE) { | 126 if (_currentMessageType != _WebSocketMessageType.NONE) { |
| 107 throw new WebSocketException("Protocol error"); | 127 throw new WebSocketException("Protocol error"); |
| 108 } | 128 } |
| 109 _currentMessageType = _WebSocketMessageType.BINARY; | 129 _currentMessageType = _WebSocketMessageType.BINARY; |
| 110 _controller = new StreamController(sync: true); | 130 _controller = new StreamController(sync: true); |
| 111 _controller.stream | 131 _controller.stream |
| 112 .fold(new BytesBuilder(), (buffer, data) => buffer..add(data)) | 132 .fold(new BytesBuilder(), (buffer, data) => buffer..add(data)) |
| 113 .then((buffer) { | 133 .then((buffer) { |
| 114 sink.add(buffer.takeBytes()); | 134 _eventSink.add(buffer.takeBytes()); |
| 115 }, onError: sink.addError); | 135 }, onError: _eventSink.addError); |
| 116 break; | 136 break; |
| 117 | 137 |
| 118 case _WebSocketOpcode.CLOSE: | 138 case _WebSocketOpcode.CLOSE: |
| 119 case _WebSocketOpcode.PING: | 139 case _WebSocketOpcode.PING: |
| 120 case _WebSocketOpcode.PONG: | 140 case _WebSocketOpcode.PONG: |
| 121 // Control frames cannot be fragmented. | 141 // Control frames cannot be fragmented. |
| 122 if (!_fin) throw new WebSocketException("Protocol error"); | 142 if (!_fin) throw new WebSocketException("Protocol error"); |
| 123 break; | 143 break; |
| 124 | 144 |
| 125 default: | 145 default: |
| 126 throw new WebSocketException("Protocol error"); | 146 throw new WebSocketException("Protocol error"); |
| 127 } | 147 } |
| 128 _state = LEN_FIRST; | 148 _state = LEN_FIRST; |
| 129 break; | 149 break; |
| 130 | 150 |
| 131 case LEN_FIRST: | 151 case LEN_FIRST: |
| 132 _masked = (byte & 0x80) != 0; | 152 _masked = (byte & 0x80) != 0; |
| 133 _len = byte & 0x7F; | 153 _len = byte & 0x7F; |
| 134 if (_isControlFrame() && _len > 125) { | 154 if (_isControlFrame() && _len > 125) { |
| 135 throw new WebSocketException("Protocol error"); | 155 throw new WebSocketException("Protocol error"); |
| 136 } | 156 } |
| 137 if (_len < 126) { | 157 if (_len < 126) { |
| 138 _lengthDone(sink); | 158 _lengthDone(); |
| 139 } else if (_len == 126) { | 159 } else if (_len == 126) { |
| 140 _len = 0; | 160 _len = 0; |
| 141 _remainingLenBytes = 2; | 161 _remainingLenBytes = 2; |
| 142 _state = LEN_REST; | 162 _state = LEN_REST; |
| 143 } else if (_len == 127) { | 163 } else if (_len == 127) { |
| 144 _len = 0; | 164 _len = 0; |
| 145 _remainingLenBytes = 8; | 165 _remainingLenBytes = 8; |
| 146 _state = LEN_REST; | 166 _state = LEN_REST; |
| 147 } | 167 } |
| 148 break; | 168 break; |
| 149 | 169 |
| 150 case LEN_REST: | 170 case LEN_REST: |
| 151 _len = _len << 8 | byte; | 171 _len = _len << 8 | byte; |
| 152 _remainingLenBytes--; | 172 _remainingLenBytes--; |
| 153 if (_remainingLenBytes == 0) { | 173 if (_remainingLenBytes == 0) { |
| 154 _lengthDone(sink); | 174 _lengthDone(); |
| 155 } | 175 } |
| 156 break; | 176 break; |
| 157 | 177 |
| 158 case MASK: | 178 case MASK: |
| 159 _maskingKey = _maskingKey << 8 | byte; | 179 _maskingKey = _maskingKey << 8 | byte; |
| 160 _remainingMaskingKeyBytes--; | 180 _remainingMaskingKeyBytes--; |
| 161 if (_remainingMaskingKeyBytes == 0) { | 181 if (_remainingMaskingKeyBytes == 0) { |
| 162 _maskDone(sink); | 182 _maskDone(); |
| 163 } | 183 } |
| 164 break; | 184 break; |
| 165 | 185 |
| 166 case PAYLOAD: | 186 case PAYLOAD: |
| 167 // The payload is not handled one byte at a time but in blocks. | 187 // The payload is not handled one byte at a time but in blocks. |
| 168 int payload; | 188 int payload; |
| 169 if (lastIndex - index <= _remainingPayloadBytes) { | 189 if (lastIndex - index <= _remainingPayloadBytes) { |
| 170 payload = lastIndex - index; | 190 payload = lastIndex - index; |
| 171 } else { | 191 } else { |
| 172 payload = _remainingPayloadBytes; | 192 payload = _remainingPayloadBytes; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 188 // Allocate a buffer for collecting the control frame | 208 // Allocate a buffer for collecting the control frame |
| 189 // payload if any. | 209 // payload if any. |
| 190 if (_controlPayload == null) { | 210 if (_controlPayload == null) { |
| 191 _controlPayload = new List<int>(); | 211 _controlPayload = new List<int>(); |
| 192 } | 212 } |
| 193 _controlPayload.addAll(buffer.sublist(index, index + payload)); | 213 _controlPayload.addAll(buffer.sublist(index, index + payload)); |
| 194 index += payload; | 214 index += payload; |
| 195 } | 215 } |
| 196 | 216 |
| 197 if (_remainingPayloadBytes == 0) { | 217 if (_remainingPayloadBytes == 0) { |
| 198 _controlFrameEnd(sink); | 218 _controlFrameEnd(); |
| 199 } | 219 } |
| 200 } else { | 220 } else { |
| 201 if (_currentMessageType != _WebSocketMessageType.TEXT && | 221 if (_currentMessageType != _WebSocketMessageType.TEXT && |
| 202 _currentMessageType != _WebSocketMessageType.BINARY) { | 222 _currentMessageType != _WebSocketMessageType.BINARY) { |
| 203 throw new WebSocketException("Protocol error"); | 223 throw new WebSocketException("Protocol error"); |
| 204 } | 224 } |
| 205 _controller.add( | 225 _controller.add( |
| 206 new Uint8List.view(buffer.buffer, index, payload)); | 226 new Uint8List.view(buffer.buffer, index, payload)); |
| 207 index += payload; | 227 index += payload; |
| 208 if (_remainingPayloadBytes == 0) { | 228 if (_remainingPayloadBytes == 0) { |
| 209 _messageFrameEnd(sink); | 229 _messageFrameEnd(); |
| 210 } | 230 } |
| 211 } | 231 } |
| 212 | 232 |
| 213 // Hack - as we always do index++ below. | 233 // Hack - as we always do index++ below. |
| 214 index--; | 234 index--; |
| 215 break; | 235 break; |
| 216 } | 236 } |
| 217 | 237 |
| 218 // Move to the next byte. | 238 // Move to the next byte. |
| 219 index++; | 239 index++; |
| 220 } | 240 } |
| 221 } catch (e) { | 241 } catch (e, stackTrace) { |
| 222 _state = FAILURE; | 242 _state = FAILURE; |
| 223 sink.addError(e); | 243 _eventSink.addError(e, stackTrace); |
| 224 } | 244 } |
| 225 } | 245 } |
| 226 | 246 |
| 227 void _lengthDone(EventSink sink) { | 247 void _lengthDone() { |
| 228 if (_masked) { | 248 if (_masked) { |
| 229 if (!_serverSide) { | 249 if (!_serverSide) { |
| 230 throw new WebSocketException("Received masked frame from server"); | 250 throw new WebSocketException("Received masked frame from server"); |
| 231 } | 251 } |
| 232 _state = MASK; | 252 _state = MASK; |
| 233 _remainingMaskingKeyBytes = 4; | 253 _remainingMaskingKeyBytes = 4; |
| 234 } else { | 254 } else { |
| 235 if (_serverSide) { | 255 if (_serverSide) { |
| 236 throw new WebSocketException("Received unmasked frame from client"); | 256 throw new WebSocketException("Received unmasked frame from client"); |
| 237 } | 257 } |
| 238 _remainingPayloadBytes = _len; | 258 _remainingPayloadBytes = _len; |
| 239 _startPayload(sink); | 259 _startPayload(); |
| 240 } | 260 } |
| 241 } | 261 } |
| 242 | 262 |
| 243 void _maskDone(EventSink sink) { | 263 void _maskDone() { |
| 244 _remainingPayloadBytes = _len; | 264 _remainingPayloadBytes = _len; |
| 245 _startPayload(sink); | 265 _startPayload(); |
| 246 } | 266 } |
| 247 | 267 |
| 248 void _startPayload(EventSink sink) { | 268 void _startPayload() { |
| 249 // If there is no actual payload perform perform callbacks without | 269 // If there is no actual payload perform perform callbacks without |
| 250 // going through the PAYLOAD state. | 270 // going through the PAYLOAD state. |
| 251 if (_remainingPayloadBytes == 0) { | 271 if (_remainingPayloadBytes == 0) { |
| 252 if (_isControlFrame()) { | 272 if (_isControlFrame()) { |
| 253 switch (_opcode) { | 273 switch (_opcode) { |
| 254 case _WebSocketOpcode.CLOSE: | 274 case _WebSocketOpcode.CLOSE: |
| 255 _state = CLOSED; | 275 _state = CLOSED; |
| 256 sink.close(); | 276 _eventSink.close(); |
| 257 break; | 277 break; |
| 258 case _WebSocketOpcode.PING: | 278 case _WebSocketOpcode.PING: |
| 259 sink.add(new _WebSocketPing()); | 279 _eventSink.add(new _WebSocketPing()); |
| 260 break; | 280 break; |
| 261 case _WebSocketOpcode.PONG: | 281 case _WebSocketOpcode.PONG: |
| 262 sink.add(new _WebSocketPong()); | 282 _eventSink.add(new _WebSocketPong()); |
| 263 break; | 283 break; |
| 264 } | 284 } |
| 265 _prepareForNextFrame(); | 285 _prepareForNextFrame(); |
| 266 } else { | 286 } else { |
| 267 _messageFrameEnd(sink); | 287 _messageFrameEnd(); |
| 268 } | 288 } |
| 269 } else { | 289 } else { |
| 270 _state = PAYLOAD; | 290 _state = PAYLOAD; |
| 271 } | 291 } |
| 272 } | 292 } |
| 273 | 293 |
| 274 void _messageFrameEnd(EventSink sink) { | 294 void _messageFrameEnd() { |
| 275 if (_fin) { | 295 if (_fin) { |
| 276 switch (_currentMessageType) { | 296 switch (_currentMessageType) { |
| 277 case _WebSocketMessageType.TEXT: | 297 case _WebSocketMessageType.TEXT: |
| 278 _controller.close(); | 298 _controller.close(); |
| 279 break; | 299 break; |
| 280 case _WebSocketMessageType.BINARY: | 300 case _WebSocketMessageType.BINARY: |
| 281 _controller.close(); | 301 _controller.close(); |
| 282 break; | 302 break; |
| 283 } | 303 } |
| 284 _controller = null; | 304 _controller = null; |
| 285 _currentMessageType = _WebSocketMessageType.NONE; | 305 _currentMessageType = _WebSocketMessageType.NONE; |
| 286 } | 306 } |
| 287 _prepareForNextFrame(); | 307 _prepareForNextFrame(); |
| 288 } | 308 } |
| 289 | 309 |
| 290 void _controlFrameEnd(EventSink sink) { | 310 void _controlFrameEnd() { |
| 291 switch (_opcode) { | 311 switch (_opcode) { |
| 292 case _WebSocketOpcode.CLOSE: | 312 case _WebSocketOpcode.CLOSE: |
| 293 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 313 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
| 294 if (_controlPayload.length > 0) { | 314 if (_controlPayload.length > 0) { |
| 295 if (_controlPayload.length == 1) { | 315 if (_controlPayload.length == 1) { |
| 296 throw new WebSocketException("Protocol error"); | 316 throw new WebSocketException("Protocol error"); |
| 297 } | 317 } |
| 298 closeCode = _controlPayload[0] << 8 | _controlPayload[1]; | 318 closeCode = _controlPayload[0] << 8 | _controlPayload[1]; |
| 299 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { | 319 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { |
| 300 throw new WebSocketException("Protocol error"); | 320 throw new WebSocketException("Protocol error"); |
| 301 } | 321 } |
| 302 if (_controlPayload.length > 2) { | 322 if (_controlPayload.length > 2) { |
| 303 closeReason = UTF8.decode(_controlPayload.sublist(2)); | 323 closeReason = UTF8.decode(_controlPayload.sublist(2)); |
| 304 } | 324 } |
| 305 } | 325 } |
| 306 _state = CLOSED; | 326 _state = CLOSED; |
| 307 sink.close(); | 327 _eventSink.close(); |
| 308 break; | 328 break; |
| 309 | 329 |
| 310 case _WebSocketOpcode.PING: | 330 case _WebSocketOpcode.PING: |
| 311 sink.add(new _WebSocketPing(_controlPayload)); | 331 _eventSink.add(new _WebSocketPing(_controlPayload)); |
| 312 break; | 332 break; |
| 313 | 333 |
| 314 case _WebSocketOpcode.PONG: | 334 case _WebSocketOpcode.PONG: |
| 315 sink.add(new _WebSocketPong(_controlPayload)); | 335 _eventSink.add(new _WebSocketPong(_controlPayload)); |
| 316 break; | 336 break; |
| 317 } | 337 } |
| 318 _prepareForNextFrame(); | 338 _prepareForNextFrame(); |
| 319 } | 339 } |
| 320 | 340 |
| 321 bool _isControlFrame() { | 341 bool _isControlFrame() { |
| 322 return _opcode == _WebSocketOpcode.CLOSE || | 342 return _opcode == _WebSocketOpcode.CLOSE || |
| 323 _opcode == _WebSocketOpcode.PING || | 343 _opcode == _WebSocketOpcode.PING || |
| 324 _opcode == _WebSocketOpcode.PONG; | 344 _opcode == _WebSocketOpcode.PONG; |
| 325 } | 345 } |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 } | 453 } |
| 434 String key = request.headers.value("Sec-WebSocket-Key"); | 454 String key = request.headers.value("Sec-WebSocket-Key"); |
| 435 if (key == null) { | 455 if (key == null) { |
| 436 return false; | 456 return false; |
| 437 } | 457 } |
| 438 return true; | 458 return true; |
| 439 } | 459 } |
| 440 } | 460 } |
| 441 | 461 |
| 442 | 462 |
| 443 class _WebSocketOutgoingTransformer extends StreamEventTransformer { | 463 // TODO(ajohnsen): Make this transformer reusable. |
| 464 class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { |
| 444 final _WebSocketImpl webSocket; | 465 final _WebSocketImpl webSocket; |
| 466 EventSink _eventSink; |
| 445 | 467 |
| 446 _WebSocketOutgoingTransformer(_WebSocketImpl this.webSocket); | 468 _WebSocketOutgoingTransformer(_WebSocketImpl this.webSocket); |
| 447 | 469 |
| 448 void handleData(message, EventSink<List<int>> sink) { | 470 Stream bind(Stream stream) { |
| 471 return new Stream.eventTransformed( |
| 472 stream, |
| 473 (EventSink eventSink) { |
| 474 if (_eventSink != null) { |
| 475 throw new StateError("WebSocket transformer already used"); |
| 476 } |
| 477 _eventSink = eventSink; |
| 478 return this; |
| 479 }); |
| 480 } |
| 481 |
| 482 void add(message) { |
| 449 if (message is _WebSocketPong) { | 483 if (message is _WebSocketPong) { |
| 450 addFrame(_WebSocketOpcode.PONG, message.payload, sink); | 484 addFrame(_WebSocketOpcode.PONG, message.payload); |
| 451 return; | 485 return; |
| 452 } | 486 } |
| 453 if (message is _WebSocketPing) { | 487 if (message is _WebSocketPing) { |
| 454 addFrame(_WebSocketOpcode.PING, message.payload, sink); | 488 addFrame(_WebSocketOpcode.PING, message.payload); |
| 455 return; | 489 return; |
| 456 } | 490 } |
| 457 List<int> data; | 491 List<int> data; |
| 458 int opcode; | 492 int opcode; |
| 459 if (message != null) { | 493 if (message != null) { |
| 460 if (message is String) { | 494 if (message is String) { |
| 461 opcode = _WebSocketOpcode.TEXT; | 495 opcode = _WebSocketOpcode.TEXT; |
| 462 data = UTF8.encode(message); | 496 data = UTF8.encode(message); |
| 463 } else { | 497 } else { |
| 464 if (message is !List<int>) { | 498 if (message is !List<int>) { |
| 465 throw new ArgumentError(message); | 499 throw new ArgumentError(message); |
| 466 } | 500 } |
| 467 opcode = _WebSocketOpcode.BINARY; | 501 opcode = _WebSocketOpcode.BINARY; |
| 468 data = message; | 502 data = message; |
| 469 } | 503 } |
| 470 } else { | 504 } else { |
| 471 opcode = _WebSocketOpcode.TEXT; | 505 opcode = _WebSocketOpcode.TEXT; |
| 472 } | 506 } |
| 473 addFrame(opcode, data, sink); | 507 addFrame(opcode, data); |
| 474 } | 508 } |
| 475 | 509 |
| 476 void handleDone(EventSink<List<int>> sink) { | 510 void addError(Object error, [StackTrace stackTrace]) { |
| 511 _eventSink.addError(error, stackTrace); |
| 512 } |
| 513 |
| 514 void close() { |
| 477 int code = webSocket._outCloseCode; | 515 int code = webSocket._outCloseCode; |
| 478 String reason = webSocket._outCloseReason; | 516 String reason = webSocket._outCloseReason; |
| 479 List<int> data; | 517 List<int> data; |
| 480 if (code != null) { | 518 if (code != null) { |
| 481 data = new List<int>(); | 519 data = new List<int>(); |
| 482 data.add((code >> 8) & 0xFF); | 520 data.add((code >> 8) & 0xFF); |
| 483 data.add(code & 0xFF); | 521 data.add(code & 0xFF); |
| 484 if (reason != null) { | 522 if (reason != null) { |
| 485 data.addAll(UTF8.encode(reason)); | 523 data.addAll(UTF8.encode(reason)); |
| 486 } | 524 } |
| 487 } | 525 } |
| 488 addFrame(_WebSocketOpcode.CLOSE, data, sink); | 526 addFrame(_WebSocketOpcode.CLOSE, data); |
| 489 sink.close(); | 527 _eventSink.close(); |
| 490 } | 528 } |
| 491 | 529 |
| 492 void addFrame(int opcode, List<int> data, EventSink<List<int>> sink) { | 530 void addFrame(int opcode, List<int> data) { |
| 493 createFrame(opcode, data, webSocket._serverSide).forEach(sink.add); | 531 createFrame(opcode, data, webSocket._serverSide).forEach(_eventSink.add); |
| 494 } | 532 } |
| 495 | 533 |
| 496 static Iterable createFrame(int opcode, List<int> data, bool serverSide) { | 534 static Iterable createFrame(int opcode, List<int> data, bool serverSide) { |
| 497 bool mask = !serverSide; // Masking not implemented for server. | 535 bool mask = !serverSide; // Masking not implemented for server. |
| 498 int dataLength = data == null ? 0 : data.length; | 536 int dataLength = data == null ? 0 : data.length; |
| 499 // Determine the header size. | 537 // Determine the header size. |
| 500 int headerSize = (mask) ? 6 : 2; | 538 int headerSize = (mask) ? 6 : 2; |
| 501 if (dataLength > 65535) { | 539 if (dataLength > 65535) { |
| 502 headerSize += 8; | 540 headerSize += 8; |
| 503 } else if (dataLength > 125) { | 541 } else if (dataLength > 125) { |
| (...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 890 (code < WebSocketStatus.NORMAL_CLOSURE || | 928 (code < WebSocketStatus.NORMAL_CLOSURE || |
| 891 code == WebSocketStatus.RESERVED_1004 || | 929 code == WebSocketStatus.RESERVED_1004 || |
| 892 code == WebSocketStatus.NO_STATUS_RECEIVED || | 930 code == WebSocketStatus.NO_STATUS_RECEIVED || |
| 893 code == WebSocketStatus.ABNORMAL_CLOSURE || | 931 code == WebSocketStatus.ABNORMAL_CLOSURE || |
| 894 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && | 932 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && |
| 895 code < WebSocketStatus.RESERVED_1015) || | 933 code < WebSocketStatus.RESERVED_1015) || |
| 896 (code >= WebSocketStatus.RESERVED_1015 && | 934 (code >= WebSocketStatus.RESERVED_1015 && |
| 897 code < 3000)); | 935 code < 3000)); |
| 898 } | 936 } |
| 899 } | 937 } |
| OLD | NEW |