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

Side by Side Diff: runtime/bin/websocket_impl.dart

Issue 10392195: Add support for websocket PING and PONG frames (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 7 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
« runtime/bin/io.dart ('K') | « runtime/bin/io.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 final String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 5 final String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
6 6
7 class _WebSocketMessageType { 7 class _WebSocketMessageType {
8 static final int NONE = 0; 8 static final int NONE = 0;
9 static final int BINARY = 1; 9 static final int BINARY = 1;
10 static final int TEXT = 2; 10 static final int TEXT = 2;
11 static final int CLOSE = 3;
12 } 11 }
13 12
14 13
15 class _WebSocketOpcode { 14 class _WebSocketOpcode {
16 static final int CONTINUATION = 0; 15 static final int CONTINUATION = 0;
17 static final int TEXT = 1; 16 static final int TEXT = 1;
18 static final int BINARY = 2; 17 static final int BINARY = 2;
19 static final int RESERVED_3 = 3; 18 static final int RESERVED_3 = 3;
20 static final int RESERVED_4 = 4; 19 static final int RESERVED_4 = 4;
21 static final int RESERVED_5 = 5; 20 static final int RESERVED_5 = 5;
(...skipping 25 matching lines...) Expand all
47 class _WebSocketProtocolProcessor { 46 class _WebSocketProtocolProcessor {
48 static final int START = 0; 47 static final int START = 0;
49 static final int LEN_FIRST = 1; 48 static final int LEN_FIRST = 1;
50 static final int LEN_REST = 2; 49 static final int LEN_REST = 2;
51 static final int MASK = 3; 50 static final int MASK = 3;
52 static final int PAYLOAD = 4; 51 static final int PAYLOAD = 4;
53 static final int CLOSED = 5; 52 static final int CLOSED = 5;
54 static final int FAILURE = 6; 53 static final int FAILURE = 6;
55 54
56 _WebSocketProtocolProcessor() { 55 _WebSocketProtocolProcessor() {
57 _reset(); 56 _prepareForNextFrame();
58 _currentMessageType = _WebSocketMessageType.NONE; 57 _currentMessageType = _WebSocketMessageType.NONE;
59 } 58 }
60 59
61 /** 60 /**
62 * Process data received from the underlying communication channel. 61 * Process data received from the underlying communication channel.
63 */ 62 */
64 void update(List<int> buffer, int offset, int count) { 63 void update(List<int> buffer, int offset, int count) {
65 int index = offset; 64 int index = offset;
66 int lastIndex = offset + count; 65 int lastIndex = offset + count;
67 try { 66 try {
(...skipping 30 matching lines...) Expand all
98 if (_currentMessageType != _WebSocketMessageType.NONE) { 97 if (_currentMessageType != _WebSocketMessageType.NONE) {
99 throw new WebSocketException("Protocol error"); 98 throw new WebSocketException("Protocol error");
100 } 99 }
101 _currentMessageType = _WebSocketMessageType.BINARY; 100 _currentMessageType = _WebSocketMessageType.BINARY;
102 if (onMessageStart != null) { 101 if (onMessageStart != null) {
103 onMessageStart(_WebSocketMessageType.BINARY); 102 onMessageStart(_WebSocketMessageType.BINARY);
104 } 103 }
105 break; 104 break;
106 105
107 case _WebSocketOpcode.CLOSE: 106 case _WebSocketOpcode.CLOSE:
108 if (_currentMessageType != _WebSocketMessageType.NONE) {
109 throw new WebSocketException("Protocol error");
110 }
111 _currentMessageType = _WebSocketMessageType.CLOSE;
112 break;
113
114 case _WebSocketOpcode.PING: 107 case _WebSocketOpcode.PING:
115 // TODO(sgjesse): Handle ping.
116 throw new UnsupportedOperationException("Web socket PING");
117 break;
118
119 case _WebSocketOpcode.PONG: 108 case _WebSocketOpcode.PONG:
120 // TODO(sgjesse): Handle pong. 109 // Control frames cannot be fragmented.
121 throw new UnsupportedOperationException("Web socket PONG"); 110 if (!_fin) throw new WebSocketException("Protocol error");
122 break; 111 break;
123 112
124 default: 113 default:
125 throw new WebSocketException("Protocol error"); 114 throw new WebSocketException("Protocol error");
126 break; 115 break;
127 } 116 }
128 _state = LEN_FIRST; 117 _state = LEN_FIRST;
129 break; 118 break;
130 119
131 case LEN_FIRST: 120 case LEN_FIRST:
132 _masked = (byte & 0x80) != 0; 121 _masked = (byte & 0x80) != 0;
133 _len = byte & 0x7F; 122 _len = byte & 0x7F;
123 if (_isControlFrame() && _len > 126) {
124 throw new WebSocketException("Protocol error");
125 }
134 if (_len < 126) { 126 if (_len < 126) {
135 _lengthDone(); 127 _lengthDone();
136 } else if (_len == 126) { 128 } else if (_len == 126) {
137 _len = 0; 129 _len = 0;
138 _remainingLenBytes = 2; 130 _remainingLenBytes = 2;
139 _state = LEN_REST; 131 _state = LEN_REST;
140 } else if (_len == 127) { 132 } else if (_len == 127) {
141 _len = 0; 133 _len = 0;
142 _remainingLenBytes = 8; 134 _remainingLenBytes = 8;
143 _state = LEN_REST; 135 _state = LEN_REST;
(...skipping 17 matching lines...) Expand all
161 break; 153 break;
162 154
163 case PAYLOAD: 155 case PAYLOAD:
164 // The payload is not handled one byte at a time but in blocks. 156 // The payload is not handled one byte at a time but in blocks.
165 int payload; 157 int payload;
166 if (lastIndex - index <= _remainingPayloadBytes) { 158 if (lastIndex - index <= _remainingPayloadBytes) {
167 payload = lastIndex - index; 159 payload = lastIndex - index;
168 } else { 160 } else {
169 payload = _remainingPayloadBytes; 161 payload = _remainingPayloadBytes;
170 } 162 }
163 _remainingPayloadBytes -= payload;
164
171 // Unmask payload if masked. 165 // Unmask payload if masked.
172 if (_masked) { 166 if (_masked) {
173 for (int i = 0; i < payload; i++) { 167 for (int i = 0; i < payload; i++) {
174 int maskingByte = 168 int maskingByte =
175 ((_maskingKey >> ((3 - _unmaskingIndex) * 8)) & 0xFF); 169 ((_maskingKey >> ((3 - _unmaskingIndex) * 8)) & 0xFF);
176 buffer[index + i] = buffer[index + i] ^ maskingByte; 170 buffer[index + i] = buffer[index + i] ^ maskingByte;
177 _unmaskingIndex = (_unmaskingIndex + 1) % 4; 171 _unmaskingIndex = (_unmaskingIndex + 1) % 4;
178 } 172 }
179 } 173 }
180 174
181 switch (_currentMessageType) { 175 if (_isControlFrame()) {
182 case _WebSocketMessageType.NONE: 176 if (payload > 0) {
183 throw new WebSocketException("Protocol error"); 177 // Allocate a buffer for collecting the control frame
184 break; 178 // payload if any.
179 if (_controlPayload == null) {
180 _controlPayload = new List<int>();
181 }
182 _controlPayload.addAll(buffer.getRange(index, payload));
183 index += payload;
184 }
185 185
186 case _WebSocketMessageType.TEXT: 186 if (_remainingPayloadBytes == 0) {
187 case _WebSocketMessageType.BINARY: 187 _controlFrameEnd();
188 if (onMessageData != null) { 188 }
189 onMessageData(buffer, index, payload); 189 } else {
190 } 190 switch (_currentMessageType) {
191 _remainingPayloadBytes -= payload; 191 case _WebSocketMessageType.NONE:
192 index += payload; 192 throw new WebSocketException("Protocol error");
193 if (_remainingPayloadBytes == 0) { 193 break;
194 _frameEnd();
195 }
196 break;
197 194
198 case _WebSocketMessageType.CLOSE: 195 case _WebSocketMessageType.TEXT:
199 // Allocate a buffer for holding the close payload if any. 196 case _WebSocketMessageType.BINARY:
200 if (_closePayload == null) { 197 if (onMessageData != null) {
201 _closePayload = new List<int>(); 198 onMessageData(buffer, index, payload);
202 }
203 _closePayload.addAll(buffer.getRange(index, payload));
204 _remainingPayloadBytes -= payload;
205 index += payload;
206 if (_fin) {
207 if (_remainingPayloadBytes != 0) {
208 throw new WebSocketException("Protocol error");
209 } 199 }
210 int status; 200 index += payload;
211 String reason; 201 if (_remainingPayloadBytes == 0) {
212 if (_closePayload.length > 0) { 202 _messageFrameEnd();
213 if (_closePayload.length == 1) {
214 throw new WebSocketException("Protocol error");
215 }
216 status = _closePayload[0] << 8 | _closePayload[1];
217 if (_closePayload.length > 2) {
218 var decoder = _StringDecoders.decoder(Encoding.UTF_8);
219 decoder.write(_closePayload.getRange(
220 2, _closePayload.length - 2));
221 reason = decoder.decoded;
222 }
223 } 203 }
224 if (onClosed != null) onClosed(status, reason); 204 break;
225 _currentMessageType = _WebSocketMessageType.NONE;
226 _state = CLOSED;
227 }
228 break;
229 205
230 default: 206 default:
231 throw new WebSocketException("Protocol error"); 207 throw new WebSocketException("Protocol error");
232 break; 208 break;
209
210 }
233 } 211 }
234 212
235 // Hack - as we always do index++ below. 213 // Hack - as we always do index++ below.
236 index--; 214 index--;
237 break;
238
239 default:
240 throw new WebSocketException("Protocol error");
241 break;
242 } 215 }
243 216
244 // Move to the next byte. 217 // Move to the next byte.
245 index++; 218 index++;
246 } 219 }
247 } catch (var e) { 220 } catch (var e) {
248 _reportError(e); 221 _reportError(e);
249 } 222 }
250 } 223 }
251 224
(...skipping 15 matching lines...) Expand all
267 _startPayload(); 240 _startPayload();
268 } 241 }
269 } 242 }
270 243
271 void _maskDone() { 244 void _maskDone() {
272 _remainingPayloadBytes = _len; 245 _remainingPayloadBytes = _len;
273 _startPayload(); 246 _startPayload();
274 } 247 }
275 248
276 void _startPayload() { 249 void _startPayload() {
277 // Check whether there is any payload. If not indicate empty 250 // Check whether there is any payload. If not handle callbacks
Anders Johnsen 2012/05/22 08:54:34 Weird sentence.
Søren Gjesse 2012/05/22 10:34:28 Rephrased.
278 // message or close without state and reason. 251 // without going through the PAYLOAD state..
279 if (_remainingPayloadBytes == 0) { 252 if (_remainingPayloadBytes == 0) {
280 if (_currentMessageType ==_WebSocketMessageType.CLOSE) { 253 if (_isControlFrame()) {
281 if (onClosed != null) onClosed(null, null); 254 switch (_opcode) {
282 _state = CLOSED; 255 case _WebSocketOpcode.CLOSE:
256 if (onClosed != null) onClosed(null, null);
257 _state = CLOSED;
258 break;
259 case _WebSocketOpcode.PING:
260 if (onPing != null) onPing(null);
261 break;
262 case _WebSocketOpcode.PONG:
263 if (onPong != null) onPong(null);
264 break;
265 }
266 _prepareForNextFrame();
283 } else { 267 } else {
284 _frameEnd(); 268 _messageFrameEnd();
285 } 269 }
286 } else { 270 } else {
287 _state = PAYLOAD; 271 _state = PAYLOAD;
288 } 272 }
289 } 273 }
290 274
291 void _frameEnd() { 275 void _messageFrameEnd() {
292 if (_remainingPayloadBytes != 0) {
293 throw new WebSocketException("Protocol error");
294 }
295 if (_fin) { 276 if (_fin) {
296 if (onMessageEnd != null) onMessageEnd(); 277 if (onMessageEnd != null) onMessageEnd();
297 _currentMessageType = _WebSocketMessageType.NONE; 278 _currentMessageType = _WebSocketMessageType.NONE;
298 } 279 }
299 _reset(); 280 _prepareForNextFrame();
300 } 281 }
301 282
302 void _reset() { 283 void _controlFrameEnd() {
303 _state = START; 284 switch (_opcode) {
285 case _WebSocketOpcode.CLOSE:
286 int status;
287 String reason;
288 if (_controlPayload.length > 0) {
289 if (_controlPayload.length == 1) {
290 throw new WebSocketException("Protocol error");
291 }
292 status = _controlPayload[0] << 8 | _controlPayload[1];
293 if (_controlPayload.length > 2) {
294 var decoder = _StringDecoders.decoder(Encoding.UTF_8);
295 decoder.write(_controlPayload.getRange(
296 2, _controlPayload.length - 2));
Mads Ager (google) 2012/05/22 09:04:13 Maybe just use four-space indentation here. The ar
Søren Gjesse 2012/05/22 10:34:28 Done.
297 reason = decoder.decoded;
298 }
299 }
300 if (onClosed != null) onClosed(status, reason);
301 _state = CLOSED;
302 break;
303
304 case _WebSocketOpcode.PING:
305 if (onPing != null) onPing(_controlPayload);
306 break;
307
308 case _WebSocketOpcode.PONG:
309 if (onPong != null) onPong(_controlPayload);
310 break;
311 }
312 _prepareForNextFrame();
313 }
314
315 bool _isControlFrame() {
316 return _opcode == _WebSocketOpcode.CLOSE ||
317 _opcode == _WebSocketOpcode.PING ||
318 _opcode == _WebSocketOpcode.PONG;
319 }
320
321 void _prepareForNextFrame() {
322 if (_state != CLOSED && _state != FAILURE) _state = START;
304 _fin = null; 323 _fin = null;
305 _opcode = null; 324 _opcode = null;
306 _len = null; 325 _len = null;
307 _masked = null; 326 _masked = null;
308 _maskingKey = 0; 327 _maskingKey = 0;
309 _remainingLenBytes = null; 328 _remainingLenBytes = null;
310 _remainingMaskingKeyBytes = null; 329 _remainingMaskingKeyBytes = null;
311 _remainingPayloadBytes = null; 330 _remainingPayloadBytes = null;
312 _unmaskingIndex = 0; 331 _unmaskingIndex = 0;
332 _controlPayload = null;
313 } 333 }
314 334
315 void _reportError(e) { 335 void _reportError(e) {
316 // Report the error through the error callback if any. Otherwise 336 // Report the error through the error callback if any. Otherwise
317 // throw the error. 337 // throw the error.
318 if (onError != null) { 338 if (onError != null) {
319 onError(e); 339 onError(e);
320 _state = FAILURE; 340 _state = FAILURE;
321 } else { 341 } else {
322 throw e; 342 throw e;
323 } 343 }
324 } 344 }
325 345
326 int _state; 346 int _state;
327 bool _fin; 347 bool _fin;
328 int _opcode; 348 int _opcode;
329 int _len; 349 int _len;
330 bool _masked; 350 bool _masked;
331 int _maskingKey; 351 int _maskingKey;
332 int _remainingLenBytes; 352 int _remainingLenBytes;
333 int _remainingMaskingKeyBytes; 353 int _remainingMaskingKeyBytes;
334 int _remainingPayloadBytes; 354 int _remainingPayloadBytes;
335 int _unmaskingIndex; 355 int _unmaskingIndex;
336 356
337 int _currentMessageType; 357 int _currentMessageType;
338 List<int> _closePayload; 358 List<int> _controlPayload;
339 359
340 Function onMessageStart; 360 Function onMessageStart;
341 Function onMessageData; 361 Function onMessageData;
342 Function onMessageEnd; 362 Function onMessageEnd;
363 Function onPing;
364 Function onPong;
343 Function onClosed; 365 Function onClosed;
344 Function onError; 366 Function onError;
345 } 367 }
346 368
347 369
348 class _WebSocketConnectionBase { 370 class _WebSocketConnectionBase {
349 void _socketConnected(Socket socket) { 371 void _socketConnected(Socket socket) {
350 _socket = socket; 372 _socket = socket;
351 _socket.onError = (e) { 373 _socket.onError = (e) {
352 _reportError(e); 374 _reportError(e);
353 _socket.close(); 375 _socket.close();
354 }; 376 };
355 } 377 }
356 378
357 void _startProcessing(List<int> unparsedData) { 379 void _startProcessing(List<int> unparsedData) {
358 _WebSocketProtocolProcessor processor = new _WebSocketProtocolProcessor(); 380 _WebSocketProtocolProcessor processor = new _WebSocketProtocolProcessor();
359 processor.onMessageStart = _onWebSocketMessageStart; 381 processor.onMessageStart = _onWebSocketMessageStart;
360 processor.onMessageData = _onWebSocketMessageData; 382 processor.onMessageData = _onWebSocketMessageData;
361 processor.onMessageEnd = _onWebSocketMessageEnd; 383 processor.onMessageEnd = _onWebSocketMessageEnd;
384 processor.onPing = _onWebSocketPing;
385 processor.onPong = _onWebSocketPong;
362 processor.onClosed = _onWebSocketClosed; 386 processor.onClosed = _onWebSocketClosed;
363 processor.onError = _onWebSocketError; 387 processor.onError = _onWebSocketError;
364 if (unparsedData != null) { 388 if (unparsedData != null) {
365 processor.update(unparsedData, 0, unparsedData.length); 389 processor.update(unparsedData, 0, unparsedData.length);
366 } 390 }
367 _socket.onData = () { 391 _socket.onData = () {
368 int available = _socket.available(); 392 int available = _socket.available();
369 List<int> data = new List<int>(available); 393 List<int> data = new List<int>(available);
370 int read = _socket.readList(data, 0, available); 394 int read = _socket.readList(data, 0, available);
371 processor.update(data, 0, read); 395 processor.update(data, 0, read);
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 if (_currentMessageType == _WebSocketMessageType.TEXT) { 503 if (_currentMessageType == _WebSocketMessageType.TEXT) {
480 _onMessage(_decoder.decoded); 504 _onMessage(_decoder.decoded);
481 } else { 505 } else {
482 _onMessage(_outputStream.contents()); 506 _onMessage(_outputStream.contents());
483 } 507 }
484 } 508 }
485 _decoder = null; 509 _decoder = null;
486 _outputStream = null; 510 _outputStream = null;
487 } 511 }
488 512
513 _onWebSocketPing(List<int> payload) {
514 _sendFrame(_WebSocketOpcode.PONG, payload);
515 }
516
517 _onWebSocketPong(List<int> payload) {
518 // Currently pong messages are ignored.
519 }
520
489 _onWebSocketClosed(int status, String reason) { 521 _onWebSocketClosed(int status, String reason) {
490 _closeReceived = true; 522 _closeReceived = true;
491 if (_onClosed != null) _onClosed(status, reason); 523 if (_onClosed != null) _onClosed(status, reason);
492 if (_closeSent) { 524 if (_closeSent) {
493 // Got close frame in response to close frame. Now close the socket. 525 // Got close frame in response to close frame. Now close the socket.
494 if (_closeTimer != null) _closeTimer.cancel(); 526 if (_closeTimer != null) _closeTimer.cancel();
495 _socket.close(); 527 _socket.close();
496 } else { 528 } else {
497 close(status); 529 close(status);
498 } 530 }
499 } 531 }
500 532
501 _onWebSocketError(e) { 533 _onWebSocketError(e) {
502 _reportError(e); 534 _reportError(e);
503 _socket.close(); 535 _socket.close();
504 } 536 }
505 537
506 _sendFrame(int opcode, List<int> data) { 538 _sendFrame(int opcode, [List<int> data]) {
507 bool mask = false; // Masking not implemented for server. 539 bool mask = false; // Masking not implemented for server.
508 int dataLength = data == null ? 0 : data.length; 540 int dataLength = data == null ? 0 : data.length;
509 // Determine the header size. 541 // Determine the header size.
510 int headerSize = (mask) ? 6 : 2; 542 int headerSize = (mask) ? 6 : 2;
511 if (dataLength > 65535) { 543 if (dataLength > 65535) {
512 headerSize += 8; 544 headerSize += 8;
513 } else if (dataLength > 125) { 545 } else if (dataLength > 125) {
514 headerSize += 2; 546 headerSize += 2;
515 } 547 }
516 List<int> header = new List<int>(headerSize); 548 List<int> header = new List<int>(headerSize);
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 881
850 class _WebSocketCloseEvent implements CloseEvent { 882 class _WebSocketCloseEvent implements CloseEvent {
851 _WebSocketCloseEvent(this._wasClean, this._code, this._reason); 883 _WebSocketCloseEvent(this._wasClean, this._code, this._reason);
852 bool get wasClean() => _wasClean; 884 bool get wasClean() => _wasClean;
853 int get code() => _code; 885 int get code() => _code;
854 String get reason() => _reason; 886 String get reason() => _reason;
855 bool _wasClean; 887 bool _wasClean;
856 int _code; 888 int _code;
857 String _reason; 889 String _reason;
858 } 890 }
OLDNEW
« runtime/bin/io.dart ('K') | « runtime/bin/io.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698