PageRenderTime 96ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/chrome/content/socket.js

https://github.com/tpurtell/socialbar
JavaScript | 390 lines | 193 code | 56 blank | 141 comment | 32 complexity | 12f96461a1e8d6446f90cd9ab5f0b464 MD5 | raw file
  1. // from mozilla jslib with a few tweaks
  2. function
  3. Socket ()
  4. {
  5. if (arguments.length)
  6. this.init(arguments[0]);
  7. else
  8. this.init();
  9. }
  10. /**
  11. * function: available
  12. * purpose: number of bytes waiting to be read
  13. *
  14. * interface: available()
  15. *
  16. * returns: integer : number of bytes waiting to be read
  17. */
  18. Socket.prototype.available = function ()
  19. {
  20. if (!this.isAlive())
  21. return 0;
  22. var bytesAvailable = 0;
  23. try {
  24. bytesAvailable = this._inputInterface.available();
  25. } catch(exception) {
  26. this.isConnected = false;
  27. this._exception = exception;
  28. }
  29. return bytesAvailable;
  30. }
  31. /**
  32. * function: close
  33. * purpose: closes the socket connection
  34. * interface: close()
  35. *
  36. * returns: nothing.
  37. */
  38. Socket.prototype.close = function ()
  39. {
  40. if (!this.isOpen()) return;
  41. this.isOpenFlag = false;
  42. this.isConnected = false;
  43. this._connect_cb = function() {}
  44. // calls to _inputStream.close() and _outputStream.close() didn't
  45. // function.
  46. if(this._transport)
  47. this._transport.close(0);
  48. }
  49. /**
  50. * function: init
  51. * purpose: provides initialization for the Socket class.
  52. * interface: socket([classID])
  53. * arguments: classID : classesByID class-identifier (see Socket for info)
  54. * returns: nothing.
  55. */
  56. Socket.prototype.init = function ()
  57. {
  58. this.isOpenFlag = false;
  59. this.isConnected = false;
  60. this._connect_cb = function() {}
  61. this.openInputFlags = 0;
  62. this.openInputSegmentSize = 0;
  63. this.openInputSegmentCount = 0;
  64. this.openOutputFlags = 0;
  65. this.openOutputSegmentSize = 0;
  66. this.openOutputSegmentCount = 0;
  67. var defaultContractID = "@mozilla.org/network/socket-transport-service;1";
  68. // var defaultClassID = "{c07e81e0-ef12-11d2-92b6-00105a1b0d64}"
  69. var socketServiceClass;
  70. switch (arguments.length)
  71. {
  72. case 0:
  73. socketServiceClass = Components.classes[defaultContractID];
  74. break;
  75. case 1:
  76. socketServiceClass = Components.classesByID[arguments[0]];
  77. break;
  78. default:
  79. throw( "Socket.init: unexpected arguments" );
  80. break;
  81. }
  82. if (!socketServiceClass)
  83. throw ("Socket constructor: Couldn't get socket service class.");
  84. var socketService = socketServiceClass.getService();
  85. if (!socketService)
  86. throw ("Socket constructor: Couldn't get socket service.");
  87. this._socketService = socketService.QueryInterface(Components.interfaces.nsISocketTransportService);
  88. }
  89. /**
  90. * function: isAlive
  91. * purpose: tests the connection to see if it still works.
  92. * interface: isAlive()
  93. * arguments: none.
  94. * returns: boolean : true if the connection still works.
  95. *
  96. * Note:
  97. * This function is not accurate if invoked in the same javascript
  98. * stack crawl as open().
  99. */
  100. Socket.prototype.isAlive = function ()
  101. {
  102. this.isConnected = ( this.isOpen() ? this._transport.isAlive() : false );
  103. return this.isConnected;
  104. }
  105. /**
  106. * function: isOpen
  107. * purpose: returns true if the socket has been opened (which is
  108. * different from connect). Multiple invocations of open will
  109. * fail if each old connection is not closed first.
  110. * interface: isOpen()
  111. * arguments: none.
  112. * returns: boolean : true if open() has been invoked without close.
  113. */
  114. Socket.prototype.isOpen = function () { return this.isOpenFlag; }
  115. /**
  116. * function: open
  117. * purpose: opens the socket
  118. * interface: open( host, port [ binary ] )
  119. *
  120. * arguments: host : String, host to connect to
  121. * port : int, port number to use
  122. * binary : optional, use binary input support, defaults false
  123. *
  124. * returns: nothing.
  125. */
  126. Socket.prototype.open = function (host, port, type, on_connect)
  127. {
  128. if (this.isOpen()) return;
  129. if(type == undefined)
  130. type = null;
  131. if(typeof type == "string")
  132. type = [type];
  133. this.host = host.toLowerCase();
  134. this.port = port;
  135. this.binary = (arguments.length > 2) ? arguments[ 2 ] : false;
  136. this.isOpenFlag = true;
  137. // in theory, we'd look up proxy information here. but we're being
  138. // a bare socket so....
  139. // create the transport:
  140. // socketTypes = null
  141. // typeCount = 0
  142. // host
  143. // port
  144. // proxy-info = null
  145. this._transport = this._socketService.createTransport(type, type == null ? 0 : type.length,
  146. host, port, null);
  147. if (!this._transport)
  148. throw ("Socket.open: Error opening transport.");
  149. var tm = Components.classes["@mozilla.org/thread-manager;1"].getService(Components.interfaces.nsIThreadManager);
  150. if(on_connect)
  151. this._connect_cb = on_connect;
  152. this._transport.setEventSink(this, tm.mainThread);
  153. var openFlags = (this.blocking ) && ( typeof document == "object")
  154. ? 0
  155. : Components.interfaces.nsITransport.OPEN_BLOCKING;
  156. this._inputStream = this._transport.openInputStream(
  157. this.openInputFlags,
  158. this.openInputSegmentSize,
  159. this.openInputSegmentCount);
  160. if (!this._inputStream)
  161. throw ("Socket.open: Error getting input stream.");
  162. if (this.binary)
  163. this._inputInterface = this.toBinaryInputStream( this._inputStream );
  164. else
  165. this._inputInterface = this.toScriptableInputStream( this._inputStream );
  166. this._outputStream = this._transport.openOutputStream(
  167. this.openOutputFlags,
  168. this.openOutputSegmentSize,
  169. this.openOutputSegmentCount );
  170. if (!this._outputStream)
  171. throw ("Socket.open: Error getting output stream.");
  172. // We really should call _transport.isAlive (?) but that is never reliable
  173. // (either always false or always true).
  174. // Experimentation shows that calls to available() or isAlive() will not
  175. // catch any problems with the connection until the javascript call
  176. // stack has completely unwound.
  177. this.isConnected = true;
  178. }
  179. /**
  180. * function: read
  181. * purpose: reads data from a socket.
  182. *
  183. * interface: read(bytes)
  184. *
  185. * arguments: bytes : integer, number of bytes to read in
  186. *
  187. * returns: string
  188. *
  189. * Note:
  190. * will only return the smaller of specified vs available bytes.
  191. *
  192. */
  193. Socket.prototype.read = function (bytes)
  194. {
  195. if (!this.isAlive())
  196. throw "Socket.read: Not Connected.";
  197. var rv = new String;
  198. if (bytes == 0)
  199. return rv;
  200. var availableBytes = this.available();
  201. if (availableBytes == 0)
  202. return rv;
  203. bytes = Math.min(availableBytes, bytes);
  204. if (bytes) {
  205. if (this.binary)
  206. // despite the documentation, this call works
  207. rv = this._inputInterface.readBytes( bytes );
  208. else
  209. rv = this._inputInterface.read( bytes );
  210. }
  211. return rv;
  212. }
  213. /**
  214. * function: write
  215. * purpose: writes the given string to the socket.
  216. *
  217. * interface: write(str)
  218. *
  219. * arguments: str : string to be written.
  220. *
  221. * returns: integer : number of bytes written.
  222. */
  223. Socket.prototype.write = function (str)
  224. {
  225. if (!this.isAlive())
  226. throw "Socket.write: Not Connected.";
  227. var rv = 0;
  228. try {
  229. rv = this._outputStream.write(str, str.length);
  230. } catch (e) { this.isConnected = false; }
  231. return rv;
  232. }
  233. /*
  234. * function: toBinaryInputStream
  235. * purpose: creates an nsIBinaryInputStream wrapper around the given
  236. * inputStream.
  237. *
  238. * interface: toBinaryInputStream(inputStream)
  239. *
  240. * arguments: inputStream : result of openInputStream
  241. *
  242. * returns: nsIBinaryInputStream
  243. */
  244. Socket.prototype.toBinaryInputStream = function (inputStream)
  245. {
  246. var rv = Components.classes["@mozilla.org/binaryinputstream;1"]
  247. .getService(Components.interfaces.nsIBinaryInputStream);
  248. rv.setInputStream(inputStream);
  249. return rv;
  250. }
  251. /**
  252. * function: toScriptableInputStream
  253. * purpose: creates an nsIScriptableInputStream wrapper around the given
  254. * inputStream.
  255. *
  256. * interface: toScriptableInputStream( inputStream )
  257. *
  258. * arguments: inputStream : result of openInputStream
  259. *
  260. * returns: nsIScriptableInputStream
  261. */
  262. Socket.prototype.toScriptableInputStream = function (inputStream)
  263. {
  264. var rv = Components.classes["@mozilla.org/scriptableinputstream;1"]
  265. .getService(Components.interfaces.nsIScriptableInputStream);
  266. rv.init( inputStream );
  267. return rv;
  268. }
  269. Socket.prototype.onTransportStatus =
  270. function(aTransport, aStatus, aProgress, aProgressMax)
  271. {
  272. if(aStatus == Components.interfaces.nsISocketTransport.STATUS_CONNECTED_TO)
  273. this._connect_cb();
  274. }
  275. /**
  276. * function: async
  277. * purpose:
  278. * interface:
  279. *
  280. * returns:
  281. * usage:
  282. *
  283. * var aSocket = new Socket;
  284. * var observer = {
  285. * streamStarted: function (socketContext){ }, //onstart action
  286. * streamStopped: function (socketContext, status){ }, //onstop action
  287. * receiveData: function (data){alert(data)}
  288. * }
  289. * aSocket.open("ftp.mozilla.org", 21);
  290. * aSocket.async(observer );
  291. *
  292. */
  293. Socket.prototype.async = function (observer)
  294. {
  295. // to preserve ourselves within necko/async
  296. this.wrappedJSObject = this;
  297. this._pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]
  298. .createInstance(Components.interfaces.nsIInputStreamPump);
  299. this._pump.init(this._inputStream, -1, -1, 0, 0, false);
  300. this._pump.asyncRead(new SocketListener(observer), this);
  301. }
  302. // async callbacks
  303. function SocketListener(observer) { this._observer = observer; }
  304. SocketListener.prototype.onStartRequest =
  305. function (channel, socketContext)
  306. {
  307. theSocket = socketContext.wrappedJSObject;
  308. this._observer.streamStarted( theSocket );
  309. }
  310. SocketListener.prototype.onStopRequest =
  311. function (channel, socketContext, status, errorMsg)
  312. {
  313. theSocket = socketContext.wrappedJSObject;
  314. this._observer.streamStopped( theSocket, status );
  315. }
  316. SocketListener.prototype.onDataAvailable =
  317. function (channel, socketContext, inputStream, sourceOffset, count)
  318. {
  319. theSocket = socketContext.wrappedJSObject;
  320. // try and maintain the connection
  321. // but read here because Socket.read will fail with HTTP requests due
  322. // to the socket being closed.
  323. theSocket._inputStream = inputStream;
  324. if (theSocket.binary) {
  325. theSocket._inputInterface =
  326. theSocket.toBinaryInputStream( theSocket._inputStream );
  327. // despite the documentation, readBytes works
  328. this._observer.receiveData( theSocket._inputInterface.readBytes(count) );
  329. } else {
  330. theSocket._inputInterface =
  331. theSocket.toScriptableInputStream( theSocket._inputStream );
  332. this._observer.receiveData( theSocket._inputInterface.read(count) );
  333. }
  334. }