PageRenderTime 62ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/socket.js

https://github.com/astro/bitford
JavaScript | 223 lines | 189 code | 30 blank | 4 comment | 41 complexity | 70a05776bbe88849adfaddb8eb2cc2de MD5 | raw file
  1. var Socket = chrome.socket || chrome.experimental.socket;
  2. function BaseSocket(sockId) {
  3. this.sockId = sockId;
  4. this.paused = true;
  5. this.readPending = false;
  6. }
  7. BaseSocket.prototype = {
  8. getInfo: function(cb) {
  9. chrome.socket.getInfo(this.sockId, cb);
  10. },
  11. end: function() {
  12. if (!this.sockId)
  13. return;
  14. if (this.onEnd)
  15. this.onEnd();
  16. Socket.destroy(this.sockId);
  17. delete this.sockId;
  18. },
  19. pause: function() {
  20. this.paused = true;
  21. },
  22. resume: function() {
  23. this.paused = false;
  24. this.read();
  25. },
  26. connect: function(host, port, cb) {
  27. chrome.socket.connect(this.sockId, host, port, function(res) {
  28. if (res === 0)
  29. cb(null);
  30. else
  31. cb(new Error("Connect: " + res));
  32. });
  33. },
  34. read: function() {
  35. if (this.paused || this.readPending)
  36. return;
  37. this.readPending = true;
  38. Socket.read(this.sockId, this.readLength, function(readInfo) {
  39. this.readPending = false;
  40. this.readLength = undefined;
  41. if (readInfo.resultCode < 0)
  42. return this.end();
  43. if (readInfo.data && this.onData) {
  44. try {
  45. this.onData(readInfo.data);
  46. /* onData() could have closed it */
  47. if (this.sockId)
  48. this.read();
  49. } catch (e) {
  50. console.error(e.stack || e.message || e);
  51. this.end();
  52. }
  53. }
  54. }.bind(this));
  55. }
  56. };
  57. /* Creates paused sockets */
  58. function createTCPServer(host, port, acceptCb, listenCb) {
  59. var backlog = 1;
  60. Socket.create('tcp', {}, function(createInfo) {
  61. var sockId = createInfo.socketId;
  62. Socket.listen(sockId, host, port, backlog, function(res) {
  63. function loop() {
  64. Socket.accept(sockId, function(acceptInfo) {
  65. var sockId = acceptInfo.socketId;
  66. if (sockId) {
  67. var sock = new TCPSocket(sockId);
  68. acceptCb(sock);
  69. }
  70. loop();
  71. });
  72. }
  73. if (res >= 0) {
  74. if (listenCb)
  75. listenCb();
  76. loop();
  77. } else {
  78. console.error("Cannot listen on", host, ":", port, ":", res);
  79. if (listenCb)
  80. listenCb(new Error("Listen: " + res));
  81. }
  82. });
  83. });
  84. }
  85. function tryCreateTCPServer(port, acceptCb, listenCb) {
  86. var attempt = 0;
  87. function doTry() {
  88. attempt++;
  89. createTCPServer("::", port, acceptCb, function(err) {
  90. if (err) {
  91. if (attempt < 100) {
  92. port += 1 + Math.floor(7 * Math.random());
  93. doTry();
  94. } else {
  95. listenCb(err);
  96. }
  97. } else {
  98. listenCb(null, port);
  99. }
  100. });
  101. }
  102. doTry();
  103. }
  104. /* Creates paused sockets */
  105. function connectTCP(host, port, cb) {
  106. Socket.create('tcp', {}, function(createInfo) {
  107. var sock = new TCPSocket(createInfo.socketId);
  108. sock.connect(host, port, function(err) {
  109. cb(err, err ? null : sock);
  110. });
  111. });
  112. }
  113. /* TODO: use an event emitter */
  114. function TCPSocket(sockId) {
  115. BaseSocket.call(this, sockId);
  116. this.writesPending = 0;
  117. this.drained = true;
  118. }
  119. TCPSocket.prototype = Object.create(BaseSocket.prototype);
  120. TCPSocket.prototype.constructor = TCPSocket;
  121. TCPSocket.prototype.write = function(data) {
  122. if (!this.sockId)
  123. return;
  124. if (typeof data === 'string')
  125. data = strToUTF8Arr(data);
  126. if (data.buffer)
  127. data = data.buffer;
  128. Socket.write(this.sockId, data, function(writeInfo) {
  129. if (writeInfo.bytesWritten < 0) {
  130. console.warn("Write to socket", this.sockId, ":", writeInfo.bytesWritten);
  131. return this.end();
  132. }
  133. this.writesPending--;
  134. if (this.writesPending < 1 && this.sockId) {
  135. this.drained = true;
  136. if (this.onDrain)
  137. this.onDrain();
  138. }
  139. }.bind(this));
  140. this.writesPending++;
  141. this.drained = false;
  142. };
  143. TCPSocket.prototype.end = function() {
  144. if (this.sockId) {
  145. Socket.disconnect(this.sockId);
  146. BaseSocket.prototype.end.call(this, arguments);
  147. }
  148. };
  149. function connectUDP(host, port, cb) {
  150. Socket.create('udp', {}, function(createInfo) {
  151. var sock = new UDPSocket(createInfo.socketId);
  152. sock.connect(host, port, function(err) {
  153. try {
  154. cb(err, err ? null : sock);
  155. } catch (e) {
  156. console.warn(e.stack || e.message || e);
  157. }
  158. });
  159. });
  160. }
  161. function UDPSocket(sockId) {
  162. BaseSocket.call(this, sockId);
  163. }
  164. UDPSocket.prototype = Object.create(BaseSocket.prototype);
  165. UDPSocket.prototype.constructor = UDPSocket;
  166. UDPSocket.prototype.write = function(data) {
  167. if (!this.sockId)
  168. return;
  169. if (typeof data === 'string')
  170. data = strToUTF8Arr(data);
  171. else if (data.buffer)
  172. data = data.buffer;
  173. Socket.write(this.sockId, data, function(writeInfo) {
  174. if (writeInfo.bytesWritten < 0) {
  175. console.warn("Write to socket", this.sockId, ":", writeInfo.bytesWritten);
  176. return this.end();
  177. }
  178. }.bind(this));
  179. };
  180. UDPSocket.prototype.recvLoop = function() {
  181. chrome.socket.recvFrom(this.sockId, function(recvFromInfo) {
  182. if (recvFromInfo.resultCode > 0 && this.onData) {
  183. try {
  184. this.onData(recvFromInfo.data, recvFromInfo.address, recvFromInfo.port);
  185. } catch (e) {
  186. console.error(e.stack || e.message || e);
  187. }
  188. this.recvLoop();
  189. } else {
  190. console.warn("UDPSocket", this.sockId, "recvFrom", recvFromInfo);
  191. }
  192. }.bind(this));
  193. };