PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/socket.js

https://github.com/akzhan/common-node
JavaScript | 243 lines | 100 code | 21 blank | 122 comment | 7 complexity | 17b02a09593858d8aff745d3ee2c0dc8 MD5 | raw file
  1. /**
  2. * @fileoverview Socket class as defined in the [CommonJS
  3. * Sockets/A](http://wiki.commonjs.org/wiki/Sockets/A) proposal.
  4. */
  5. var Fiber = require('fibers');
  6. var net = require('net');
  7. var io = require('./io');
  8. exports.Socket = Socket;
  9. var families = {
  10. AF_INET: "tcp4",
  11. AF_INET6: "tcp6",
  12. AF_UNIX: "unix"
  13. };
  14. /**
  15. * The Socket class is used to create a blocking socket.
  16. *
  17. * @constructor
  18. * @this {Socket}
  19. * @param family AF_INET (default), AF_INET6 or AF_UNIX
  20. * @param type SOCK_STREAM for TCP (default) or SOCK_DGRAM for UDP
  21. */
  22. function Socket(family, type) {
  23. if(!(this instanceof Socket))
  24. return new Socket(family, type);
  25. if(family instanceof net.Socket) {
  26. this.client = family;
  27. } else {
  28. this.type = families[type];
  29. this.guts = {};
  30. }
  31. }
  32. /**
  33. * Initiate a connection on a socket. Connect to a remote port on the specified
  34. * host with a connection timeout. Throws an exception in case of failure.
  35. *
  36. * @param host IP address or hostname
  37. * @param port port number or service name
  38. * @param timeout timeout value (default X microseconds)
  39. */
  40. Socket.prototype.connect = function(host, port, timeout) {
  41. var fiber = Fiber.current;
  42. // TODO Unix sockets
  43. // TODO UDP sockets
  44. // TODO TLS sockets
  45. var client = this.client = net.createConnection(port, host);
  46. client.on('connect', function() {
  47. fiber.run();
  48. });
  49. client.on('error', function(error) {
  50. fiber.run(error);
  51. });
  52. var result = Fiber.yield();
  53. if(result instanceof Error) {
  54. throw new Error(result.message);
  55. }
  56. return this;
  57. };
  58. /**
  59. * When a new socket is created, it has no address bound to it. Bind assigns the
  60. * specified address (also known as name) to the socket. Throws an exception in
  61. * case of failure.
  62. *
  63. * @param host address (interface) to which the socket will be bound. If address
  64. * is omitted, any address is will match.
  65. * @param port port number or service name to which socket is to be bound. If
  66. * port is undefined, the socket wont bind to any port.
  67. */
  68. Socket.prototype.bind = function(host, port) {
  69. var server = this.server = net.createServer();
  70. var fiber = Fiber.current;
  71. server.on('error', function(error) {
  72. // on error EADDRINUSE
  73. fiber.run(error);
  74. });
  75. // TODO leave out port or host to use defaults / random
  76. server.listen(port, host, function() {
  77. fiber.run();
  78. });
  79. // TODO add a listener to queue up connections until accept is called
  80. var result = Fiber.yield();
  81. if(result instanceof Error) {
  82. throw new Error(result.message);
  83. }
  84. return this;
  85. };
  86. //TODO add functions to retrieve port and hostname the socket is bound to
  87. /**
  88. * Accept a connection on a socket. Returns a new (connected) Socket.
  89. */
  90. Socket.prototype.accept = function() {
  91. // TODO grab an already accepted connection if there are any?
  92. var fiber = Fiber.current;
  93. var onconnection = function(socket) {
  94. fiber.run(socket);
  95. };
  96. this.server.on('connection', onconnection);
  97. var result = Fiber.yield();
  98. this.server.removeListener('connection', onconnection);
  99. return new Socket(result);
  100. };
  101. /**
  102. * Listen for incoming connections on a socket (use before an accept). Throws an
  103. * exception in case of failure.
  104. */
  105. Socket.prototype.listen = function() {
  106. // TODO figure out what the purpose of this is?
  107. return this;
  108. };
  109. /**
  110. * Shut down part of a full-duplex connection on a socket. If [what] is SHUT_RD,
  111. * further receives will be disallowed. If [what] is SHUT_WR further sends will
  112. * be disallowed. If what is SHUT_RDWR, further sends and receives will be
  113. * disallowed.
  114. *
  115. * @param what SHUT_RD, SHUT_WR or SHUT_RDWR
  116. */
  117. Socket.prototype.shutdown = function(what) {
  118. // ???
  119. };
  120. /**
  121. * Close the socket immediately
  122. */
  123. Socket.prototype.close = function() {
  124. // socket.end([data], [encoding]);
  125. // socket.destroy();
  126. };
  127. Socket.prototype.getStream = function() {
  128. if(!this.stream) {
  129. this.stream = new io.Stream(this.client || this.server);
  130. }
  131. return this.stream;
  132. };
  133. /**
  134. * Receive a block of bytes from the socket. Returns block of bytes read.
  135. *
  136. * @param maxlen number of bytes to read. Default: X bytes
  137. */
  138. Socket.prototype.read = function(maxlen) {
  139. return this.getStream().read(maxlen);
  140. };
  141. /**
  142. * Receive a block of bytes from the socket. Returns block of bytes read. May
  143. * receive fewer bytes than requested even if the end of the stream hasn’t been
  144. * reached.
  145. *
  146. * @param maxlen number of bytes to read. Default: X bytes
  147. */
  148. Socket.prototype.receive = function(maxlen) {
  149. // TODO no clear what to do here, but the solution below is clearly broken; we
  150. // could just deprecate this method
  151. return this.getStream().read(null);
  152. };
  153. /**
  154. * Send a block of bytes to the socket.
  155. *
  156. * @param data block of bytes to send
  157. */
  158. Socket.prototype.send = function(data) {
  159. // socket.write(data, [encoding], [callback])
  160. // socket.write(data, [encoding], [fileDescriptor], [callback])
  161. // TODO deal with the fileDescriptor case
  162. this.getStream().write(data);
  163. return this;
  164. };
  165. /**
  166. * Send a block of bytes to the socket. May send only a part of the data even if
  167. * the peer hasn’t closed connection.
  168. *
  169. * @param data block of bytes to send
  170. */
  171. Socket.prototype.write = function(data) {
  172. // socket.write(data, [encoding], [callback])
  173. // socket.write(data, [encoding], [fileDescriptor], [callback])
  174. // TODO deal with the fileDescriptor case
  175. // TODO this is clearly broken - we could just deprecate this method
  176. this.getStream().write(data);
  177. return this;
  178. };
  179. /**
  180. * Receive all data from socket. Returns object with ip_address and port of
  181. * sender along with data properties.
  182. *
  183. * @param maxlen number of bytes to read. Default: X bytes
  184. */
  185. Socket.prototype.receiveFrom = function(maxlen) {
  186. // UDP?
  187. };
  188. /**
  189. * Send a block of data to a specific host and port.
  190. *
  191. * @param host IP address or hostname
  192. * @param port port number or service name
  193. * @param data block of bytes to send
  194. */
  195. Socket.prototype.sendTo = function(host, port, data) {
  196. // UDP?
  197. };
  198. /**
  199. * Sends a complete File object across a socket.
  200. *
  201. * @param file a File object
  202. * @deprecated
  203. */
  204. Socket.prototype.sendFile = function(file) {
  205. throw new Error('sendFile is deprecated');
  206. };
  207. /**
  208. * Set socket option value to socket option name.
  209. *
  210. * @param option
  211. * @param value
  212. */
  213. Socket.prototype.setOption = function(option, value) {
  214. this.guts[option] = value;
  215. return this;
  216. };
  217. /**
  218. * Get socket option value, option should be socket option name.
  219. */
  220. Socket.prototype.getOption = function() {
  221. return this.guts[option];
  222. };