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

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

Issue 11337019: Use patching for dart:io. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments Created 8 years, 1 month 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
6
7 class _WebSocketMessageType {
8 static const int NONE = 0;
9 static const int BINARY = 1;
10 static const int TEXT = 2;
11 }
12
13
14 class _WebSocketOpcode {
15 static const int CONTINUATION = 0;
16 static const int TEXT = 1;
17 static const int BINARY = 2;
18 static const int RESERVED_3 = 3;
19 static const int RESERVED_4 = 4;
20 static const int RESERVED_5 = 5;
21 static const int RESERVED_6 = 6;
22 static const int RESERVED_7 = 7;
23 static const int CLOSE = 8;
24 static const int PING = 9;
25 static const int PONG = 10;
26 static const int RESERVED_B = 11;
27 static const int RESERVED_C = 12;
28 static const int RESERVED_D = 13;
29 static const int RESERVED_E = 14;
30 static const int RESERVED_F = 15;
31 }
32
33 /**
34 * The web socket protocol processor handles the protocol byte stream
35 * which is supplied through the [:update:] and [:closed:]
36 * methods. As the protocol is processed the following callbacks are
37 * called:
38 *
39 * [:onMessageStart:]
40 * [:onMessageData:]
41 * [:onMessageEnd:]
42 * [:onClosed:]
43 * [:onError:]
44 *
45 */
46 class _WebSocketProtocolProcessor {
47 static const int START = 0;
48 static const int LEN_FIRST = 1;
49 static const int LEN_REST = 2;
50 static const int MASK = 3;
51 static const int PAYLOAD = 4;
52 static const int CLOSED = 5;
53 static const int FAILURE = 6;
54
55 _WebSocketProtocolProcessor() {
56 _prepareForNextFrame();
57 _currentMessageType = _WebSocketMessageType.NONE;
58 }
59
60 /**
61 * Process data received from the underlying communication channel.
62 */
63 void update(List<int> buffer, int offset, int count) {
64 int index = offset;
65 int lastIndex = offset + count;
66 try {
67 if (_state == CLOSED) {
68 throw new WebSocketException("Data on closed connection");
69 }
70 if (_state == FAILURE) {
71 throw new WebSocketException("Data on failed connection");
72 }
73 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) {
74 int byte = buffer[index];
75 switch (_state) {
76 case START:
77 _fin = (byte & 0x80) != 0;
78 _opcode = (byte & 0xF);
79 switch (_opcode) {
80 case _WebSocketOpcode.CONTINUATION:
81 if (_currentMessageType == _WebSocketMessageType.NONE) {
82 throw new WebSocketException("Protocol error");
83 }
84 break;
85
86 case _WebSocketOpcode.TEXT:
87 if (_currentMessageType != _WebSocketMessageType.NONE) {
88 throw new WebSocketException("Protocol error");
89 }
90 _currentMessageType = _WebSocketMessageType.TEXT;
91 if (onMessageStart !== null) {
92 onMessageStart(_WebSocketMessageType.TEXT);
93 }
94 break;
95
96 case _WebSocketOpcode.BINARY:
97 if (_currentMessageType != _WebSocketMessageType.NONE) {
98 throw new WebSocketException("Protocol error");
99 }
100 _currentMessageType = _WebSocketMessageType.BINARY;
101 if (onMessageStart !== null) {
102 onMessageStart(_WebSocketMessageType.BINARY);
103 }
104 break;
105
106 case _WebSocketOpcode.CLOSE:
107 case _WebSocketOpcode.PING:
108 case _WebSocketOpcode.PONG:
109 // Control frames cannot be fragmented.
110 if (!_fin) throw new WebSocketException("Protocol error");
111 break;
112
113 default:
114 throw new WebSocketException("Protocol error");
115 }
116 _state = LEN_FIRST;
117 break;
118
119 case LEN_FIRST:
120 _masked = (byte & 0x80) != 0;
121 _len = byte & 0x7F;
122 if (_isControlFrame() && _len > 126) {
123 throw new WebSocketException("Protocol error");
124 }
125 if (_len < 126) {
126 _lengthDone();
127 } else if (_len == 126) {
128 _len = 0;
129 _remainingLenBytes = 2;
130 _state = LEN_REST;
131 } else if (_len == 127) {
132 _len = 0;
133 _remainingLenBytes = 8;
134 _state = LEN_REST;
135 }
136 break;
137
138 case LEN_REST:
139 _len = _len << 8 | byte;
140 _remainingLenBytes--;
141 if (_remainingLenBytes == 0) {
142 _lengthDone();
143 }
144 break;
145
146 case MASK:
147 _maskingKey = _maskingKey << 8 | byte;
148 _remainingMaskingKeyBytes--;
149 if (_remainingMaskingKeyBytes == 0) {
150 _maskDone();
151 }
152 break;
153
154 case PAYLOAD:
155 // The payload is not handled one byte at a time but in blocks.
156 int payload;
157 if (lastIndex - index <= _remainingPayloadBytes) {
158 payload = lastIndex - index;
159 } else {
160 payload = _remainingPayloadBytes;
161 }
162 _remainingPayloadBytes -= payload;
163
164 // Unmask payload if masked.
165 if (_masked) {
166 for (int i = 0; i < payload; i++) {
167 int maskingByte =
168 ((_maskingKey >> ((3 - _unmaskingIndex) * 8)) & 0xFF);
169 buffer[index + i] = buffer[index + i] ^ maskingByte;
170 _unmaskingIndex = (_unmaskingIndex + 1) % 4;
171 }
172 }
173
174 if (_isControlFrame()) {
175 if (payload > 0) {
176 // Allocate a buffer for collecting the control frame
177 // payload if any.
178 if (_controlPayload == null) {
179 _controlPayload = new List<int>();
180 }
181 _controlPayload.addAll(buffer.getRange(index, payload));
182 index += payload;
183 }
184
185 if (_remainingPayloadBytes == 0) {
186 _controlFrameEnd();
187 }
188 } else {
189 switch (_currentMessageType) {
190 case _WebSocketMessageType.NONE:
191 throw new WebSocketException("Protocol error");
192
193 case _WebSocketMessageType.TEXT:
194 case _WebSocketMessageType.BINARY:
195 if (onMessageData !== null) {
196 onMessageData(buffer, index, payload);
197 }
198 index += payload;
199 if (_remainingPayloadBytes == 0) {
200 _messageFrameEnd();
201 }
202 break;
203
204 default:
205 throw new WebSocketException("Protocol error");
206 }
207 }
208
209 // Hack - as we always do index++ below.
210 index--;
211 }
212
213 // Move to the next byte.
214 index++;
215 }
216 } catch (e) {
217 if (onClosed !== null) onClosed(WebSocketStatus.PROTOCOL_ERROR,
218 "Protocol error");
219 _state = FAILURE;
220 }
221 }
222
223 /**
224 * Indicate that the underlying communication channel has been closed.
225 */
226 void closed() {
227 if (_state == START || _state == CLOSED || _state == FAILURE) return;
228 if (onClosed !== null) onClosed(WebSocketStatus.ABNORMAL_CLOSURE,
229 "Connection closed unexpectedly");
230 _state = CLOSED;
231 }
232
233 void _lengthDone() {
234 if (_masked) {
235 _state = MASK;
236 _remainingMaskingKeyBytes = 4;
237 } else {
238 _remainingPayloadBytes = _len;
239 _startPayload();
240 }
241 }
242
243 void _maskDone() {
244 _remainingPayloadBytes = _len;
245 _startPayload();
246 }
247
248 void _startPayload() {
249 // If there is no actual payload perform perform callbacks without
250 // going through the PAYLOAD state.
251 if (_remainingPayloadBytes == 0) {
252 if (_isControlFrame()) {
253 switch (_opcode) {
254 case _WebSocketOpcode.CLOSE:
255 if (onClosed !== null) onClosed(1005, "");
256 _state = CLOSED;
257 break;
258 case _WebSocketOpcode.PING:
259 if (onPing !== null) onPing(null);
260 break;
261 case _WebSocketOpcode.PONG:
262 if (onPong !== null) onPong(null);
263 break;
264 }
265 _prepareForNextFrame();
266 } else {
267 _messageFrameEnd();
268 }
269 } else {
270 _state = PAYLOAD;
271 }
272 }
273
274 void _messageFrameEnd() {
275 if (_fin) {
276 if (onMessageEnd !== null) onMessageEnd();
277 _currentMessageType = _WebSocketMessageType.NONE;
278 }
279 _prepareForNextFrame();
280 }
281
282 void _controlFrameEnd() {
283 switch (_opcode) {
284 case _WebSocketOpcode.CLOSE:
285 int status = WebSocketStatus.NO_STATUS_RECEIVED;
286 String reason = "";
287 if (_controlPayload.length > 0) {
288 if (_controlPayload.length == 1) {
289 throw new WebSocketException("Protocol error");
290 }
291 status = _controlPayload[0] << 8 | _controlPayload[1];
292 if (status == WebSocketStatus.NO_STATUS_RECEIVED) {
293 throw new WebSocketException("Protocol error");
294 }
295 if (_controlPayload.length > 2) {
296 var decoder = _StringDecoders.decoder(Encoding.UTF_8);
297 decoder.write(
298 _controlPayload.getRange(2, _controlPayload.length - 2));
299 reason = decoder.decoded();
300 }
301 }
302 if (onClosed !== null) onClosed(status, reason);
303 _state = CLOSED;
304 break;
305
306 case _WebSocketOpcode.PING:
307 if (onPing !== null) onPing(_controlPayload);
308 break;
309
310 case _WebSocketOpcode.PONG:
311 if (onPong !== null) onPong(_controlPayload);
312 break;
313 }
314 _prepareForNextFrame();
315 }
316
317 bool _isControlFrame() {
318 return _opcode == _WebSocketOpcode.CLOSE ||
319 _opcode == _WebSocketOpcode.PING ||
320 _opcode == _WebSocketOpcode.PONG;
321 }
322
323 void _prepareForNextFrame() {
324 if (_state != CLOSED && _state != FAILURE) _state = START;
325 _fin = null;
326 _opcode = null;
327 _len = null;
328 _masked = null;
329 _maskingKey = 0;
330 _remainingLenBytes = null;
331 _remainingMaskingKeyBytes = null;
332 _remainingPayloadBytes = null;
333 _unmaskingIndex = 0;
334 _controlPayload = null;
335 }
336
337 int _state;
338 bool _fin;
339 int _opcode;
340 int _len;
341 bool _masked;
342 int _maskingKey;
343 int _remainingLenBytes;
344 int _remainingMaskingKeyBytes;
345 int _remainingPayloadBytes;
346 int _unmaskingIndex;
347
348 int _currentMessageType;
349 List<int> _controlPayload;
350
351 Function onMessageStart;
352 Function onMessageData;
353 Function onMessageEnd;
354 Function onPing;
355 Function onPong;
356 Function onClosed;
357 }
358
359
360 class _WebSocketConnectionBase {
361 void _socketConnected(Socket socket) {
362 _socket = socket;
363 _socket.onError = (e) => _socket.close();
364 }
365
366 void _startProcessing(List<int> unparsedData) {
367 _WebSocketProtocolProcessor processor = new _WebSocketProtocolProcessor();
368 processor.onMessageStart = _onWebSocketMessageStart;
369 processor.onMessageData = _onWebSocketMessageData;
370 processor.onMessageEnd = _onWebSocketMessageEnd;
371 processor.onPing = _onWebSocketPing;
372 processor.onPong = _onWebSocketPong;
373 processor.onClosed = _onWebSocketClosed;
374 if (unparsedData !== null) {
375 processor.update(unparsedData, 0, unparsedData.length);
376 }
377 _socket.onData = () {
378 int available = _socket.available();
379 List<int> data = new List<int>(available);
380 int read = _socket.readList(data, 0, available);
381 processor.update(data, 0, read);
382 };
383 _socket.onClosed = () {
384 processor.closed();
385 if (_closeSent) {
386 // Got socket close in response to close frame. Don't treat
387 // that as an error.
388 if (_closeTimer !== null) _closeTimer.cancel();
389 } else {
390 if (_onClosed !== null) _onClosed(WebSocketStatus.ABNORMAL_CLOSURE,
391 "Unexpected close");
392 }
393 _socket.close();
394 };
395 }
396
397 void set onMessage(void callback(Object message)) {
398 _onMessage = callback;
399 }
400
401 void set onClosed(void callback(int status, String reason)) {
402 _onClosed = callback;
403 }
404
405 send(message) {
406 if (_closeSent) {
407 throw new WebSocketException("Connection closed");
408 }
409 List<int> data;
410 int opcode;
411 if (message !== null) {
412 if (message is String) {
413 opcode = _WebSocketOpcode.TEXT;
414 data = _StringEncoders.encoder(Encoding.UTF_8).encodeString(message);
415 } else {
416 if (message is !List<int>) {
417 throw new ArgumentError(message);
418 }
419 opcode = _WebSocketOpcode.BINARY;
420 data = message;
421 }
422 } else {
423 opcode = _WebSocketOpcode.TEXT;
424 }
425 _sendFrame(opcode, data);
426 }
427
428 close([int status, String reason]) {
429 if (status == WebSocketStatus.RESERVED_1004 ||
430 status == WebSocketStatus.NO_STATUS_RECEIVED ||
431 status == WebSocketStatus.RESERVED_1015) {
432 throw new WebSocketException("Reserved status code $status");
433 }
434
435 if (_closeSent) return;
436 List<int> data;
437 if (status !== null) {
438 data = new List<int>();
439 data.add((status >> 8) & 0xFF);
440 data.add(status & 0xFF);
441 if (reason !== null) {
442 data.addAll(
443 _StringEncoders.encoder(Encoding.UTF_8).encodeString(reason));
444 }
445 }
446 _sendFrame(_WebSocketOpcode.CLOSE, data);
447
448 if (_closeReceived) {
449 // Close the socket when the close frame has been sent - if it
450 // does not take too long.
451 _socket.outputStream.close();
452 _socket.outputStream.onClosed = () {
453 if (_closeTimer !== null) _closeTimer.cancel();
454 _socket.close();
455 };
456 _closeTimer = new Timer(5000, (t) {
457 _socket.close();
458 });
459 } else {
460 // Half close the socket and expect a close frame in response
461 // before closing the socket. If a close frame does not arrive
462 // within a reasonable amount of time just close the socket.
463 _socket.outputStream.close();
464 _closeTimer = new Timer(5000, (t) {
465 _socket.close();
466 });
467 }
468 _closeSent = true;
469 }
470
471 int get hashCode => _hash;
472
473 _onWebSocketMessageStart(int type) {
474 _currentMessageType = type;
475 if (_currentMessageType == _WebSocketMessageType.TEXT) {
476 _decoder = _StringDecoders.decoder(Encoding.UTF_8);
477 } else {
478 _outputStream = new ListOutputStream();
479 }
480 }
481
482 _onWebSocketMessageData(List<int> buffer, int offset, int count) {
483 if (_currentMessageType == _WebSocketMessageType.TEXT) {
484 _decoder.write(buffer.getRange(offset, count));
485 } else {
486 _outputStream.write(buffer.getRange(offset, count));
487 }
488 }
489
490 _onWebSocketMessageEnd() {
491 if (_onMessage !== null) {
492 if (_currentMessageType == _WebSocketMessageType.TEXT) {
493 _onMessage(_decoder.decoded());
494 } else {
495 _onMessage(_outputStream.read());
496 }
497 }
498 _decoder = null;
499 _outputStream = null;
500 }
501
502 _onWebSocketPing(List<int> payload) {
503 _sendFrame(_WebSocketOpcode.PONG, payload);
504 }
505
506 _onWebSocketPong(List<int> payload) {
507 // Currently pong messages are ignored.
508 }
509
510 _onWebSocketClosed(int status, String reason) {
511 _closeReceived = true;
512 if (_onClosed !== null) _onClosed(status, reason);
513 if (_closeSent) {
514 // Got close frame in response to close frame. Now close the socket.
515 if (_closeTimer !== null) _closeTimer.cancel();
516 _socket.close();
517 } else {
518 if (status != WebSocketStatus.NO_STATUS_RECEIVED) {
519 close(status);
520 } else {
521 close();
522 }
523 }
524 }
525
526 _sendFrame(int opcode, [List<int> data]) {
527 bool mask = false; // Masking not implemented for server.
528 int dataLength = data == null ? 0 : data.length;
529 // Determine the header size.
530 int headerSize = (mask) ? 6 : 2;
531 if (dataLength > 65535) {
532 headerSize += 8;
533 } else if (dataLength > 125) {
534 headerSize += 2;
535 }
536 List<int> header = new List<int>(headerSize);
537 int index = 0;
538 // Set FIN and opcode.
539 header[index++] = 0x80 | opcode;
540 // Determine size and position of length field.
541 int lengthBytes = 1;
542 int firstLengthByte = 1;
543 if (dataLength > 65535) {
544 header[index++] = 127;
545 lengthBytes = 8;
546 } else if (dataLength > 125) {
547 header[index++] = 126;
548 lengthBytes = 2;
549 }
550 // Write the length in network byte order into the header.
551 for (int i = 0; i < lengthBytes; i++) {
552 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF;
553 }
554 assert(index == headerSize);
555 _socket.outputStream.write(header);
556 if (data !== null) {
557 _socket.outputStream.write(data);
558 }
559 }
560
561 Socket _socket;
562 Timer _closeTimer;
563 int _hash;
564
565 Function _onMessage;
566 Function _onClosed;
567
568 int _currentMessageType = _WebSocketMessageType.NONE;
569 _StringDecoder _decoder;
570 ListOutputStream _outputStream;
571 bool _closeReceived = false;
572 bool _closeSent = false;
573 }
574
575
576 class _WebSocketConnection
577 extends _WebSocketConnectionBase implements WebSocketConnection {
578 _WebSocketConnection(DetachedSocket detached) {
579 _hash = detached.socket.hashCode;
580 _socketConnected(detached.socket);
581 _startProcessing(detached.unparsedData);
582 }
583 }
584
585
586 class _WebSocketHandler implements WebSocketHandler {
587 void onRequest(HttpRequest request, HttpResponse response) {
588 // Check that this is a web socket upgrade.
589 if (!_isWebSocketUpgrade(request)) {
590 response.statusCode = HttpStatus.BAD_REQUEST;
591 response.outputStream.close();
592 return;
593 }
594
595 // Send the upgrade response.
596 response.statusCode = HttpStatus.SWITCHING_PROTOCOLS;
597 response.headers.add(HttpHeaders.CONNECTION, "Upgrade");
598 response.headers.add(HttpHeaders.UPGRADE, "websocket");
599 String key = request.headers.value("Sec-WebSocket-Key");
600 SHA1 sha1 = new SHA1();
601 sha1.update("$key$_webSocketGUID".charCodes);
602 String accept = _Base64._encode(sha1.digest());
603 response.headers.add("Sec-WebSocket-Accept", accept);
604 response.contentLength = 0;
605
606 // Upgrade the connection and get the underlying socket.
607 WebSocketConnection conn =
608 new _WebSocketConnection(response.detachSocket());
609 if (_onOpen !== null) _onOpen(conn);
610 }
611
612 void set onOpen(callback(WebSocketConnection connection)) {
613 _onOpen = callback;
614 }
615
616 bool _isWebSocketUpgrade(HttpRequest request) {
617 if (request.method != "GET") {
618 return false;
619 }
620 if (request.headers[HttpHeaders.CONNECTION] == null) {
621 return false;
622 }
623 bool isUpgrade = false;
624 request.headers[HttpHeaders.CONNECTION].forEach((String value) {
625 if (value.toLowerCase() == "upgrade") isUpgrade = true;
626 });
627 if (!isUpgrade) return false;
628 String upgrade = request.headers.value(HttpHeaders.UPGRADE);
629 if (upgrade == null || upgrade.toLowerCase() != "websocket") {
630 return false;
631 }
632 String version = request.headers.value("Sec-WebSocket-Version");
633 if (version == null || version != "13") {
634 return false;
635 }
636 String key = request.headers.value("Sec-WebSocket-Key");
637 if (key == null) {
638 return false;
639 }
640 return true;
641 }
642
643 Function _onOpen;
644 }
645
646
647 class _WebSocketClientConnection
648 extends _WebSocketConnectionBase implements WebSocketClientConnection {
649 _WebSocketClientConnection(HttpClientConnection this._conn,
650 [List<String> protocols]) {
651 _conn.onRequest = _onHttpClientRequest;
652 _conn.onResponse = _onHttpClientResponse;
653 _conn.onError = (e) {
654 if (_onClosed !== null) {
655 _onClosed(WebSocketStatus.ABNORMAL_CLOSURE, "$e");
656 }
657 };
658
659 // Generate the nonce now as it is also used to set the hash code.
660 _generateNonceAndHash();
661 }
662
663 void set onRequest(void callback(HttpClientRequest request)) {
664 _onRequest = callback;
665 }
666
667 void set onOpen(void callback()) {
668 _onOpen = callback;
669 }
670
671 void set onNoUpgrade(void callback(HttpClientResponse request)) {
672 _onNoUpgrade = callback;
673 }
674
675 void _onHttpClientRequest(HttpClientRequest request) {
676 if (_onRequest !== null) {
677 _onRequest(request);
678 }
679 // Setup the initial handshake.
680 request.headers.add(HttpHeaders.CONNECTION, "upgrade");
681 request.headers.set(HttpHeaders.UPGRADE, "websocket");
682 request.headers.set("Sec-WebSocket-Key", _nonce);
683 request.headers.set("Sec-WebSocket-Version", "13");
684 request.contentLength = 0;
685 request.outputStream.close();
686 }
687
688 void _onHttpClientResponse(HttpClientResponse response) {
689 if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS) {
690 if (_onNoUpgrade !== null) {
691 _onNoUpgrade(response);
692 } else {
693 _conn.detachSocket().socket.close();
694 throw new WebSocketException("Protocol upgrade refused");
695 }
696 return;
697 }
698
699 if (!_isWebSocketUpgrade(response)) {
700 _conn.detachSocket().socket.close();
701 throw new WebSocketException("Protocol upgrade failed");
702 return;
703 }
704
705 // Connection upgrade successful.
706 DetachedSocket detached = _conn.detachSocket();
707 _socketConnected(detached.socket);
708 if (_onOpen !== null) _onOpen();
709 _startProcessing(detached.unparsedData);
710 }
711
712 void _generateNonceAndHash() {
713 Random random = new Random();
714 assert(_nonce == null);
715 void intToBigEndianBytes(int value, List<int> bytes, int offset) {
716 bytes[offset] = (value >> 24) & 0xFF;
717 bytes[offset + 1] = (value >> 16) & 0xFF;
718 bytes[offset + 2] = (value >> 8) & 0xFF;
719 bytes[offset + 3] = value & 0xFF;
720 }
721
722 // Generate 16 random bytes. Use the last four bytes for the hash code.
723 List<int> nonce = new List<int>(16);
724 for (int i = 0; i < 4; i++) {
725 int r = random.nextInt(0x100000000);
726 intToBigEndianBytes(r, nonce, i * 4);
727 }
728 _nonce = _Base64._encode(nonce);
729 _hash = random.nextInt(0x100000000);
730 }
731
732 bool _isWebSocketUpgrade(HttpClientResponse response) {
733 if (response.headers[HttpHeaders.CONNECTION] == null) {
734 return false;
735 }
736 bool isUpgrade = false;
737 response.headers[HttpHeaders.CONNECTION].forEach((String value) {
738 if (value.toLowerCase() == "upgrade") isUpgrade = true;
739 });
740 if (!isUpgrade) return false;
741 String upgrade = response.headers.value(HttpHeaders.UPGRADE);
742 if (upgrade == null || upgrade.toLowerCase() != "websocket") {
743 return false;
744 }
745 String accept = response.headers.value("Sec-WebSocket-Accept");
746 if (accept == null) {
747 return false;
748 }
749 SHA1 sha1 = new SHA1();
750 sha1.update("$_nonce$_webSocketGUID".charCodes);
751 List<int> expectedAccept = sha1.digest();
752 List<int> receivedAccept = _Base64._decode(accept);
753 if (expectedAccept.length != receivedAccept.length) return false;
754 for (int i = 0; i < expectedAccept.length; i++) {
755 if (expectedAccept[i] != receivedAccept[i]) return false;
756 }
757 return true;
758 }
759
760 Function _onRequest;
761 Function _onOpen;
762 Function _onNoUpgrade;
763 HttpClientConnection _conn;
764 String _nonce;
765 }
766
767
768 class _WebSocket implements WebSocket {
769 _WebSocket(String url, [protocols]) {
770 Uri uri = new Uri.fromString(url);
771 if (uri.scheme != "ws") {
772 throw new WebSocketException("Unsupported URL scheme ${uri.scheme}");
773 }
774 if (uri.userInfo != "") {
775 throw new WebSocketException("Unsupported user info ${uri.userInfo}");
776 }
777 int port = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port;
778 String path = uri.path;
779 if (path.length == 0) path = "/";
780 if (uri.query != "") {
781 if (uri.fragment != "") {
782 path = "${path}?${uri.query}#${uri.fragment}";
783 } else {
784 path = "${path}?${uri.query}";
785 }
786 }
787
788 HttpClient client = new HttpClient();
789 HttpClientConnection conn = client.open("GET", uri.domain, port, path);
790 if (protocols is String) protocols = [protocols];
791 _wsconn = new WebSocketClientConnection(conn, protocols);
792 _wsconn.onOpen = () {
793 // HTTP client not needed after socket have been detached.
794 client.shutdown();
795 client = null;
796 _readyState = WebSocket.OPEN;
797 if (_onopen !== null) _onopen();
798 };
799 _wsconn.onMessage = (message) {
800 if (_onmessage !== null) {
801 _onmessage(new _WebSocketMessageEvent(message));
802 }
803 };
804 _wsconn.onClosed = (status, reason) {
805 _readyState = WebSocket.CLOSED;
806 if (_onclose !== null) {
807 _onclose(new _WebSocketCloseEvent(true, status, reason));
808 }
809 };
810 _wsconn.onNoUpgrade = (response) {
811 if (_onclose !== null) {
812 _onclose(
813 new _WebSocketCloseEvent(true,
814 WebSocketStatus.ABNORMAL_CLOSURE,
815 "Connection not upgraded"));
816 }
817 };
818 }
819
820 int get readyState => _readyState;
821 int get bufferedAmount => 0;
822
823 void set onopen(Function callback) {
824 _onopen = callback;
825 }
826
827 void set onerror(Function callback) {}
828
829 void set onclose(Function callback) {
830 _onclose = callback;
831 }
832
833 String get extensions => null;
834 String get protocol => null;
835
836 void close(int code, String reason) {
837 if (_readyState < WebSocket.CLOSING) _readyState = WebSocket.CLOSING;
838 _wsconn.close(code, reason);
839 }
840
841 void set onmessage(Function callback) {
842 _onmessage = callback;
843 }
844
845 void send(data) {
846 _wsconn.send(data);
847 }
848
849 WebSocketClientConnection _wsconn;
850 int _readyState = WebSocket.CONNECTING;
851 Function _onopen;
852 Function _onclose;
853 Function _onmessage;
854 }
855
856
857 class _WebSocketMessageEvent implements MessageEvent {
858 _WebSocketMessageEvent(this._data);
859 get data => _data;
860 var _data;
861 }
862
863
864 class _WebSocketCloseEvent implements CloseEvent {
865 _WebSocketCloseEvent(this._wasClean, this._code, this._reason);
866 bool get wasClean => _wasClean;
867 int get code => _code;
868 String get reason => _reason;
869 bool _wasClean;
870 int _code;
871 String _reason;
872 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698