OLD | NEW |
| (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 | |
6 class _SocketBase extends NativeFieldWrapperClass1 { | |
7 // Bit flags used when communicating between the eventhandler and | |
8 // dart code. The EVENT flags are used to indicate events of | |
9 // interest when sending a message from dart code to the | |
10 // eventhandler. When receiving a message from the eventhandler the | |
11 // EVENT flags indicate the events that actually happened. The | |
12 // COMMAND flags are used to send commands from dart to the | |
13 // eventhandler. COMMAND flags are never received from the | |
14 // eventhandler. Additional flags are used to communicate other | |
15 // information. | |
16 static const int _IN_EVENT = 0; | |
17 static const int _OUT_EVENT = 1; | |
18 static const int _ERROR_EVENT = 2; | |
19 static const int _CLOSE_EVENT = 3; | |
20 | |
21 static const int _CLOSE_COMMAND = 8; | |
22 static const int _SHUTDOWN_READ_COMMAND = 9; | |
23 static const int _SHUTDOWN_WRITE_COMMAND = 10; | |
24 | |
25 // Flag send to the eventhandler providing additional information on | |
26 // the type of the file descriptor. | |
27 static const int _LISTENING_SOCKET = 16; | |
28 static const int _PIPE = 17; | |
29 | |
30 static const int _FIRST_EVENT = _IN_EVENT; | |
31 static const int _LAST_EVENT = _CLOSE_EVENT; | |
32 | |
33 static const int _FIRST_COMMAND = _CLOSE_COMMAND; | |
34 static const int _LAST_COMMAND = _SHUTDOWN_WRITE_COMMAND; | |
35 | |
36 _SocketBase () { | |
37 _handlerMap = new List(_LAST_EVENT + 1); | |
38 _handlerMask = 0; | |
39 _canActivateHandlers = true; | |
40 _closed = true; | |
41 _EventHandler._start(); | |
42 _hashCode = _nextHashCode; | |
43 _nextHashCode = (_nextHashCode + 1) & 0xFFFFFFF; | |
44 } | |
45 | |
46 // Multiplexes socket events to the socket handlers. | |
47 void _multiplex(int event_mask) { | |
48 _canActivateHandlers = false; | |
49 for (int i = _FIRST_EVENT; i <= _LAST_EVENT; i++) { | |
50 if (((event_mask & (1 << i)) != 0)) { | |
51 if ((i == _CLOSE_EVENT) && this is _Socket && !_closed) { | |
52 _closedRead = true; | |
53 if (_closedWrite) _close(); | |
54 } | |
55 | |
56 var eventHandler = _handlerMap[i]; | |
57 if (eventHandler != null || i == _ERROR_EVENT) { | |
58 // Unregister the out handler before executing it. | |
59 if (i == _OUT_EVENT) _setHandler(i, null); | |
60 | |
61 // Don't call the in handler if there is no data available | |
62 // after all. | |
63 if ((i == _IN_EVENT) && (this is _Socket) && (available() == 0)) { | |
64 continue; | |
65 } | |
66 if (i == _ERROR_EVENT) { | |
67 _reportError(_getError(), ""); | |
68 close(); | |
69 } else { | |
70 eventHandler(); | |
71 } | |
72 } | |
73 } | |
74 } | |
75 _canActivateHandlers = true; | |
76 _activateHandlers(); | |
77 } | |
78 | |
79 void _setHandler(int event, Function callback) { | |
80 if (callback == null) { | |
81 _handlerMask &= ~(1 << event); | |
82 } else { | |
83 _handlerMask |= (1 << event); | |
84 } | |
85 _handlerMap[event] = callback; | |
86 // If the socket is only for writing then close the receive port | |
87 // when not waiting for any events. | |
88 if (this is _Socket && | |
89 _closedRead && | |
90 _handlerMask == 0 && | |
91 _handler != null) { | |
92 _handler.close(); | |
93 _handler = null; | |
94 } else { | |
95 _activateHandlers(); | |
96 } | |
97 } | |
98 | |
99 OSError _getError() native "Socket_GetError"; | |
100 int _getPort() native "Socket_GetPort"; | |
101 | |
102 void set onError(void callback(e)) { | |
103 _setHandler(_ERROR_EVENT, callback); | |
104 } | |
105 | |
106 void _activateHandlers() { | |
107 if (_canActivateHandlers && !_closed) { | |
108 if (_handlerMask == 0) { | |
109 if (_handler != null) { | |
110 _handler.close(); | |
111 _handler = null; | |
112 } | |
113 return; | |
114 } | |
115 int data = _handlerMask; | |
116 if (_isListenSocket()) { | |
117 data |= (1 << _LISTENING_SOCKET); | |
118 } else { | |
119 if (_closedRead) { data &= ~(1 << _IN_EVENT); } | |
120 if (_closedWrite) { data &= ~(1 << _OUT_EVENT); } | |
121 if (_isPipe()) data |= (1 << _PIPE); | |
122 } | |
123 _sendToEventHandler(data); | |
124 } | |
125 } | |
126 | |
127 int get port { | |
128 if (_port === null) { | |
129 _port = _getPort(); | |
130 } | |
131 return _port; | |
132 } | |
133 | |
134 void close([bool halfClose = false]) { | |
135 if (!_closed) { | |
136 if (halfClose) { | |
137 _closeWrite(); | |
138 } else { | |
139 _close(); | |
140 } | |
141 } else if (_handler != null) { | |
142 // This is to support closing sockets created but never assigned | |
143 // any actual socket. | |
144 _handler.close(); | |
145 _handler = null; | |
146 } | |
147 } | |
148 | |
149 void _closeWrite() { | |
150 if (!_closed) { | |
151 if (_closedRead) { | |
152 _close(); | |
153 } else { | |
154 _sendToEventHandler(1 << _SHUTDOWN_WRITE_COMMAND); | |
155 } | |
156 _closedWrite = true; | |
157 } | |
158 } | |
159 | |
160 void _closeRead() { | |
161 if (!_closed) { | |
162 if (_closedWrite) { | |
163 _close(); | |
164 } else { | |
165 _sendToEventHandler(1 << _SHUTDOWN_READ_COMMAND); | |
166 } | |
167 _closedRead = true; | |
168 } | |
169 } | |
170 | |
171 void _close() { | |
172 if (!_closed) { | |
173 _sendToEventHandler(1 << _CLOSE_COMMAND); | |
174 _handler.close(); | |
175 _handler = null; | |
176 _closed = true; | |
177 } | |
178 } | |
179 | |
180 void _sendToEventHandler(int data) { | |
181 if (_handler === null) { | |
182 _handler = new ReceivePort(); | |
183 _handler.receive((var message, ignored) { _multiplex(message); }); | |
184 } | |
185 assert(!_closed); | |
186 _EventHandler._sendData(this, _handler, data); | |
187 } | |
188 | |
189 bool _reportError(error, String message) { | |
190 void doReportError(Exception e) { | |
191 // Invoke the socket error callback if any. | |
192 bool reported = false; | |
193 if (_handlerMap[_ERROR_EVENT] != null) { | |
194 _handlerMap[_ERROR_EVENT](e); | |
195 reported = true; | |
196 } | |
197 // Propagate the error to any additional listeners. | |
198 reported = reported || _propagateError(e); | |
199 if (!reported) throw e; | |
200 } | |
201 | |
202 // For all errors we close the socket, call the error handler and | |
203 // disable further calls of the error handler. | |
204 close(); | |
205 if (error is OSError) { | |
206 doReportError(new SocketIOException(message, error)); | |
207 } else if (error is List) { | |
208 assert(_isErrorResponse(error)); | |
209 switch (error[0]) { | |
210 case _ILLEGAL_ARGUMENT_RESPONSE: | |
211 doReportError(new ArgumentError()); | |
212 break; | |
213 case _OSERROR_RESPONSE: | |
214 doReportError(new SocketIOException( | |
215 message, new OSError(error[2], error[1]))); | |
216 break; | |
217 default: | |
218 doReportError(new Exception("Unknown error")); | |
219 break; | |
220 } | |
221 } else { | |
222 doReportError(new SocketIOException(message)); | |
223 } | |
224 } | |
225 | |
226 int get hashCode => _hashCode; | |
227 | |
228 bool _propagateError(Exception e) => false; | |
229 | |
230 abstract bool _isListenSocket(); | |
231 abstract bool _isPipe(); | |
232 | |
233 // Is this socket closed. | |
234 bool _closed; | |
235 | |
236 // Dedicated ReceivePort for socket events. | |
237 ReceivePort _handler; | |
238 | |
239 // Poll event to handler map. | |
240 List _handlerMap; | |
241 | |
242 // Indicates for which poll events the socket registered handlers. | |
243 int _handlerMask; | |
244 | |
245 // Indicates if native interrupts can be activated. | |
246 bool _canActivateHandlers; | |
247 | |
248 // Holds the port of the socket, null if not known. | |
249 int _port; | |
250 | |
251 // Hash code for the socket. Currently this is just a counter. | |
252 int _hashCode; | |
253 static int _nextHashCode = 0; | |
254 bool _closedRead = false; | |
255 bool _closedWrite = false; | |
256 } | |
257 | |
258 | |
259 class _ServerSocket extends _SocketBase implements ServerSocket { | |
260 // Constructor for server socket. First a socket object is allocated | |
261 // in which the native socket is stored. After that _createBind | |
262 // is called which creates a file descriptor and binds the given address | |
263 // and port to the socket. Null is returned if file descriptor creation or | |
264 // bind failed. | |
265 factory _ServerSocket(String bindAddress, int port, int backlog) { | |
266 _ServerSocket socket = new _ServerSocket._internal(); | |
267 var result = socket._createBindListen(bindAddress, port, backlog); | |
268 if (result is OSError) { | |
269 socket.close(); | |
270 throw new SocketIOException("Failed to create server socket", result); | |
271 } | |
272 socket._closed = false; | |
273 assert(result); | |
274 if (port != 0) { | |
275 socket._port = port; | |
276 } | |
277 return socket; | |
278 } | |
279 | |
280 _ServerSocket._internal(); | |
281 | |
282 _accept(Socket socket) native "ServerSocket_Accept"; | |
283 | |
284 _createBindListen(String bindAddress, int port, int backlog) | |
285 native "ServerSocket_CreateBindListen"; | |
286 | |
287 void set onConnection(void callback(Socket connection)) { | |
288 _clientConnectionHandler = callback; | |
289 _setHandler(_SocketBase._IN_EVENT, | |
290 _clientConnectionHandler != null ? _connectionHandler : null); | |
291 } | |
292 | |
293 void _connectionHandler() { | |
294 if (!_closed) { | |
295 _Socket socket = new _Socket._internal(); | |
296 var result = _accept(socket); | |
297 if (result is OSError) { | |
298 _reportError(result, "Accept failed"); | |
299 } else if (result) { | |
300 socket._closed = false; | |
301 _clientConnectionHandler(socket); | |
302 } else { | |
303 // Temporary failure accepting the connection. Ignoring | |
304 // temporary failures lets us retry when we wake up with data | |
305 // on the listening socket again. | |
306 } | |
307 } | |
308 } | |
309 | |
310 bool _isListenSocket() => true; | |
311 bool _isPipe() => false; | |
312 | |
313 var _clientConnectionHandler; | |
314 } | |
315 | |
316 | |
317 class _Socket extends _SocketBase implements Socket { | |
318 static const HOST_NAME_LOOKUP = 0; | |
319 | |
320 // Constructs a new socket. During the construction an asynchronous | |
321 // host name lookup is initiated. The returned socket is not yet | |
322 // connected but ready for registration of callbacks. | |
323 factory _Socket(String host, int port) { | |
324 Socket socket = new _Socket._internal(); | |
325 _ensureSocketService(); | |
326 List request = new List(2); | |
327 request[0] = HOST_NAME_LOOKUP; | |
328 request[1] = host; | |
329 _socketService.call(request).then((response) { | |
330 if (socket._isErrorResponse(response)) { | |
331 socket._reportError(response, "Failed host name lookup"); | |
332 } else{ | |
333 var result = socket._createConnect(response, port); | |
334 if (result is OSError) { | |
335 socket.close(); | |
336 socket._reportError(result, "Connection failed"); | |
337 } else { | |
338 socket._closed = false; | |
339 socket._activateHandlers(); | |
340 } | |
341 } | |
342 }); | |
343 return socket; | |
344 } | |
345 | |
346 _Socket._internal(); | |
347 _Socket._internalReadOnly() : _pipe = true { super._closedWrite = true; } | |
348 _Socket._internalWriteOnly() : _pipe = true { super._closedRead = true; } | |
349 | |
350 int available() { | |
351 if (!_closed) { | |
352 var result = _available(); | |
353 if (result is OSError) { | |
354 _reportError(result, "Available failed"); | |
355 return 0; | |
356 } else { | |
357 return result; | |
358 } | |
359 } | |
360 throw new | |
361 SocketIOException("Error: available failed - invalid socket handle"); | |
362 } | |
363 | |
364 _available() native "Socket_Available"; | |
365 | |
366 int readList(List<int> buffer, int offset, int bytes) { | |
367 if (!_closed) { | |
368 if (bytes == 0) { | |
369 return 0; | |
370 } | |
371 if (offset < 0) { | |
372 throw new IndexOutOfRangeException(offset); | |
373 } | |
374 if (bytes < 0) { | |
375 throw new IndexOutOfRangeException(bytes); | |
376 } | |
377 if ((offset + bytes) > buffer.length) { | |
378 throw new IndexOutOfRangeException(offset + bytes); | |
379 } | |
380 var result = _readList(buffer, offset, bytes); | |
381 if (result is OSError) { | |
382 _reportError(result, "Read failed"); | |
383 return -1; | |
384 } | |
385 return result; | |
386 } | |
387 throw new | |
388 SocketIOException("Error: readList failed - invalid socket handle"); | |
389 } | |
390 | |
391 _readList(List<int> buffer, int offset, int bytes) native "Socket_ReadList"; | |
392 | |
393 int writeList(List<int> buffer, int offset, int bytes) { | |
394 if (buffer is! List || offset is! int || bytes is! int) { | |
395 throw new ArgumentError( | |
396 "Invalid arguments to writeList on Socket"); | |
397 } | |
398 if (!_closed) { | |
399 if (bytes == 0) { | |
400 return 0; | |
401 } | |
402 if (offset < 0) { | |
403 throw new IndexOutOfRangeException(offset); | |
404 } | |
405 if (bytes < 0) { | |
406 throw new IndexOutOfRangeException(bytes); | |
407 } | |
408 if ((offset + bytes) > buffer.length) { | |
409 throw new IndexOutOfRangeException(offset + bytes); | |
410 } | |
411 _BufferAndOffset bufferAndOffset = | |
412 _ensureFastAndSerializableBuffer(buffer, offset, bytes); | |
413 var result = | |
414 _writeList(bufferAndOffset.buffer, bufferAndOffset.offset, bytes); | |
415 if (result is OSError) { | |
416 _reportError(result, "Write failed"); | |
417 // If writing fails we return 0 as the number of bytes and | |
418 // report the error on the error handler. | |
419 result = 0; | |
420 } | |
421 return result; | |
422 } | |
423 throw new SocketIOException("writeList failed - invalid socket handle"); | |
424 } | |
425 | |
426 _writeList(List<int> buffer, int offset, int bytes) | |
427 native "Socket_WriteList"; | |
428 | |
429 bool _isErrorResponse(response) { | |
430 return response is List && response[0] != _SUCCESS_RESPONSE; | |
431 } | |
432 | |
433 bool _createConnect(String host, int port) native "Socket_CreateConnect"; | |
434 | |
435 void set onWrite(void callback()) { | |
436 if (_outputStream != null) throw new StreamException( | |
437 "Cannot set write handler when output stream is used"); | |
438 _clientWriteHandler = callback; | |
439 _updateOutHandler(); | |
440 } | |
441 | |
442 void set onConnect(void callback()) { | |
443 if (_seenFirstOutEvent || _outputStream != null) { | |
444 throw new StreamException( | |
445 "Cannot set connect handler when already connected"); | |
446 } | |
447 if (_outputStream != null) { | |
448 throw new StreamException( | |
449 "Cannot set connect handler when output stream is used"); | |
450 } | |
451 _clientConnectHandler = callback; | |
452 _updateOutHandler(); | |
453 } | |
454 | |
455 void set onData(void callback()) { | |
456 if (_inputStream != null) throw new StreamException( | |
457 "Cannot set data handler when input stream is used"); | |
458 _onData = callback; | |
459 } | |
460 | |
461 void set onClosed(void callback()) { | |
462 if (_inputStream != null) throw new StreamException( | |
463 "Cannot set close handler when input stream is used"); | |
464 _onClosed = callback; | |
465 } | |
466 | |
467 bool _isListenSocket() => false; | |
468 | |
469 bool _isPipe() => _pipe; | |
470 | |
471 InputStream get inputStream { | |
472 if (_inputStream == null) { | |
473 if (_handlerMap[_SocketBase._IN_EVENT] !== null || | |
474 _handlerMap[_SocketBase._CLOSE_EVENT] !== null) { | |
475 throw new StreamException( | |
476 "Cannot get input stream when socket handlers are used"); | |
477 } | |
478 _inputStream = new _SocketInputStream(this); | |
479 } | |
480 return _inputStream; | |
481 } | |
482 | |
483 OutputStream get outputStream { | |
484 if (_outputStream == null) { | |
485 if (_handlerMap[_SocketBase._OUT_EVENT] !== null) { | |
486 throw new StreamException( | |
487 "Cannot get input stream when socket handlers are used"); | |
488 } | |
489 _outputStream = new _SocketOutputStream(this); | |
490 } | |
491 return _outputStream; | |
492 } | |
493 | |
494 void set _onWrite(void callback()) { | |
495 _setHandler(_SocketBase._OUT_EVENT, callback); | |
496 } | |
497 | |
498 void set _onData(void callback()) { | |
499 _setHandler(_SocketBase._IN_EVENT, callback); | |
500 } | |
501 | |
502 void set _onClosed(void callback()) { | |
503 _setHandler(_SocketBase._CLOSE_EVENT, callback); | |
504 } | |
505 | |
506 bool _propagateError(Exception e) { | |
507 bool reported = false; | |
508 if (_inputStream != null) { | |
509 reported = reported || _inputStream._onSocketError(e); | |
510 } | |
511 if (_outputStream != null) { | |
512 reported = reported || _outputStream._onSocketError(e); | |
513 } | |
514 return reported; | |
515 } | |
516 | |
517 void _updateOutHandler() { | |
518 void firstWriteHandler() { | |
519 assert(!_seenFirstOutEvent); | |
520 _seenFirstOutEvent = true; | |
521 | |
522 // From now on the write handler is only the client write | |
523 // handler (connect handler cannot be called again). Change this | |
524 // before calling any handlers as handlers can change the | |
525 // handlers. | |
526 if (_clientWriteHandler === null) _onWrite = _clientWriteHandler; | |
527 | |
528 // First out event is socket connected event. | |
529 if (_clientConnectHandler !== null) _clientConnectHandler(); | |
530 _clientConnectHandler = null; | |
531 | |
532 // Always (even for the first out event) call the write handler. | |
533 if (_clientWriteHandler !== null) _clientWriteHandler(); | |
534 } | |
535 | |
536 if (_clientConnectHandler === null && _clientWriteHandler === null) { | |
537 _onWrite = null; | |
538 } else { | |
539 if (_seenFirstOutEvent) { | |
540 _onWrite = _clientWriteHandler; | |
541 } else { | |
542 _onWrite = firstWriteHandler; | |
543 } | |
544 } | |
545 } | |
546 | |
547 int get remotePort { | |
548 if (_remotePort === null) { | |
549 remoteHost; | |
550 } | |
551 return _remotePort; | |
552 } | |
553 | |
554 String get remoteHost { | |
555 if (_remoteHost === null) { | |
556 List peer = _getRemotePeer(); | |
557 _remoteHost = peer[0]; | |
558 _remotePort = peer[1]; | |
559 } | |
560 return _remoteHost; | |
561 } | |
562 | |
563 List _getRemotePeer() native "Socket_GetRemotePeer"; | |
564 | |
565 static SendPort _newServicePort() native "Socket_NewServicePort"; | |
566 | |
567 static void _ensureSocketService() { | |
568 if (_socketService == null) { | |
569 _socketService = _Socket._newServicePort(); | |
570 } | |
571 } | |
572 | |
573 bool _seenFirstOutEvent = false; | |
574 bool _pipe = false; | |
575 Function _clientConnectHandler; | |
576 Function _clientWriteHandler; | |
577 _SocketInputStream _inputStream; | |
578 _SocketOutputStream _outputStream; | |
579 String _remoteHost; | |
580 int _remotePort; | |
581 static SendPort _socketService; | |
582 } | |
OLD | NEW |