AsyncSocket.hx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /*
  2. * Copyright (C)2005-2019 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package sys.net;
  23. class AsyncSocket {
  24. var loop : haxe.EventLoop;
  25. var stream : hl.uv.Stream;
  26. var tcp : hl.uv.Tcp;
  27. var recv : haxe.io.Bytes;
  28. var onWriteCallb : Bool -> Void;
  29. public function new( ?loop : haxe.EventLoop ) {
  30. if( loop == null ) loop = haxe.EventLoop.current;
  31. this.loop = loop;
  32. }
  33. public function close() {
  34. stream?.close();
  35. tcp = null;
  36. stream = null;
  37. }
  38. public function write( bytes : haxe.io.Bytes, pos : Int = 0, len : Int = -1 ) {
  39. if( stream == null ) throw new haxe.io.Eof();
  40. // libuv tells us we should wait for callb before reusing buffer ?
  41. if( onWriteCallb == null ) onWriteCallb = (b) -> onWrite(!b);
  42. stream.write(bytes,onWriteCallb,pos,len);
  43. }
  44. function init(ssl) {
  45. tcp = new hl.uv.Tcp(@:privateAccess loop.getUVLoop());
  46. stream = tcp;
  47. recv = haxe.io.Bytes.alloc(0);
  48. @:privateAccess loop.wakeup();
  49. if( ssl )
  50. stream = new hl.uv.SSLStream(stream);
  51. }
  52. public function connect(host:Host, port:Int, ssl=false) {
  53. if( tcp != null ) throw new haxe.io.Eof();
  54. init(ssl);
  55. tcp.connect(host,port,function(b) {
  56. if( ssl )
  57. Std.downcast(stream,hl.uv.SSLStream).handshake(function(err) {
  58. if( err != null )
  59. onSSLError(err);
  60. onConnectRaw(err == null);
  61. });
  62. else
  63. onConnectRaw(b);
  64. });
  65. }
  66. function onConnectRaw(b : Bool) {
  67. if( !b ) {
  68. close();
  69. onDisconnect();
  70. return;
  71. }
  72. onConnect();
  73. if( stream == null ) return;
  74. stream.readStartRaw(function(data,len) {
  75. if( len < 0 ) {
  76. close();
  77. onDisconnect();
  78. } else if( len > 0 ) {
  79. @:privateAccess {
  80. recv.b = data;
  81. recv.length = len;
  82. }
  83. onData(recv, 0, len);
  84. }
  85. });
  86. }
  87. public function listen(connections:Int) {
  88. if( tcp == null ) throw new haxe.io.Eof();
  89. tcp.listen(connections,() -> onConnect());
  90. }
  91. public function bind(host:Host, port:Int, ?ssl : { hostname:String, key:sys.ssl.Key, cert:sys.ssl.Certificate } ) {
  92. if( tcp != null ) throw new haxe.io.Eof();
  93. init(ssl != null);
  94. tcp.bind(host, port);
  95. }
  96. public function accept():Null<AsyncSocket> {
  97. if( tcp == null ) throw new haxe.io.Eof();
  98. var stream = tcp.accept();
  99. if( stream == null )
  100. return null;
  101. var s = Type.createEmptyInstance(AsyncSocket);
  102. s.stream = stream;
  103. s.recv = haxe.io.Bytes.alloc(0);
  104. s.onConnectRaw(true);
  105. return s;
  106. }
  107. public function setFastSend(b:Bool) {
  108. if( tcp == null ) throw new haxe.io.Eof();
  109. tcp.noDelay(b);
  110. }
  111. public dynamic function onConnect() {
  112. }
  113. public dynamic function onDisconnect() {
  114. }
  115. public dynamic function onData( bytes : haxe.io.Bytes, pos : Int, len : Int ) {
  116. }
  117. public dynamic function onWrite( error : Bool ) {
  118. if( error ) {
  119. onDisconnect();
  120. close();
  121. }
  122. }
  123. public dynamic function onSSLError( msg : String ) {
  124. throw msg;
  125. }
  126. public function writeString( str : String ) {
  127. var buf = haxe.io.Bytes.ofString(str);
  128. write(buf, 0, buf.length);
  129. }
  130. public static function startServer( host, port, ?ssl, onClient ) {
  131. var s = new AsyncSocket();
  132. s.onDisconnect = function() {
  133. onClient(null);
  134. };
  135. s.onConnect = function() {
  136. onClient(s.accept());
  137. };
  138. s.bind(new Host(host), port, ssl);
  139. s.listen(1);
  140. return s;
  141. }
  142. }