batman-rails-flo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +29 -0
  3. data/Rakefile +2 -0
  4. data/lib/batman-rails-flo.rb +5 -0
  5. data/lib/batman_rails_flo/railtie.rb +9 -0
  6. data/lib/batman_rails_flo/tasks.rake +14 -0
  7. data/lib/batman_rails_flo/version.rb +3 -0
  8. data/vendor/assets/javascripts/batman_rails_flo/flo_server.js +67 -0
  9. data/vendor/assets/javascripts/batman_rails_flo/live_reload.js.coffee +129 -0
  10. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/CNAME +1 -0
  11. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/CONTRIBUTING.md +9 -0
  12. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/LICENSE +22 -0
  13. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/README +50 -0
  14. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/bin/cake +7 -0
  15. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/bin/coffee +7 -0
  16. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/browser.js +134 -0
  17. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/cake.js +112 -0
  18. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/coffee-script.js +335 -0
  19. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/command.js +569 -0
  20. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/grammar.js +631 -0
  21. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/helpers.js +252 -0
  22. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/index.js +11 -0
  23. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/lexer.js +926 -0
  24. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/nodes.js +3158 -0
  25. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/optparse.js +139 -0
  26. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/parser.js +724 -0
  27. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/register.js +66 -0
  28. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/repl.js +163 -0
  29. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/rewriter.js +475 -0
  30. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/scope.js +146 -0
  31. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/sourcemap.js +161 -0
  32. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/LICENSE +21 -0
  33. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/examples/pow.js +6 -0
  34. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/index.js +82 -0
  35. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/package.json +34 -0
  36. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/readme.markdown +63 -0
  37. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/chmod.js +38 -0
  38. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/clobber.js +37 -0
  39. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/mkdirp.js +28 -0
  40. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/perm.js +32 -0
  41. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/perm_sync.js +39 -0
  42. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/race.js +41 -0
  43. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/rel.js +32 -0
  44. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/return.js +25 -0
  45. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/return_sync.js +24 -0
  46. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/root.js +18 -0
  47. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/sync.js +32 -0
  48. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/umask.js +28 -0
  49. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/umask_sync.js +32 -0
  50. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/package.json +50 -0
  51. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/register.js +1 -0
  52. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/LICENSE +30 -0
  53. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/PATENTS +23 -0
  54. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/README.md +124 -0
  55. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/bin/flo +26 -0
  56. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/configure/configure.html +57 -0
  57. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/configure/configure.js +205 -0
  58. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/configure/style.css +334 -0
  59. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/connection.js +235 -0
  60. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/devtools.html +12 -0
  61. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/index.js +323 -0
  62. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/logger.js +42 -0
  63. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/logo.png +0 -0
  64. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/manifest.json +13 -0
  65. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/session.js +305 -0
  66. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/index.js +10 -0
  67. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/lib/flo.js +226 -0
  68. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/lib/server.js +94 -0
  69. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/README.md +66 -0
  70. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/index.js +437 -0
  71. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/LICENSE +23 -0
  72. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/README.md +218 -0
  73. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/minimatch.js +1055 -0
  74. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS +14 -0
  75. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/LICENSE +23 -0
  76. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/README.md +97 -0
  77. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js +252 -0
  78. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/package.json +33 -0
  79. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/test/basic.js +369 -0
  80. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/test/foreach.js +52 -0
  81. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js +50 -0
  82. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/LICENSE +27 -0
  83. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/README.md +53 -0
  84. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/bench.js +283 -0
  85. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/package.json +42 -0
  86. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/sigmund.js +39 -0
  87. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/test/basic.js +24 -0
  88. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/package.json +40 -0
  89. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/basic.js +399 -0
  90. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/brace-expand.js +33 -0
  91. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/caching.js +14 -0
  92. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/defaults.js +274 -0
  93. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/extglob-ending-with-state-char.js +8 -0
  94. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/lib/walker.js +111 -0
  95. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/license +13 -0
  96. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/lib/makeerror.js +87 -0
  97. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/node_modules/tmpl/lib/tmpl.js +17 -0
  98. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/node_modules/tmpl/package.json +31 -0
  99. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/node_modules/tmpl/readme.md +10 -0
  100. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/package.json +34 -0
  101. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/readme.md +77 -0
  102. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/package.json +36 -0
  103. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/readme.md +52 -0
  104. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/LICENSE +55 -0
  105. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/main.js +128 -0
  106. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/package.json +34 -0
  107. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/readme.mkd +72 -0
  108. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/d/d/t +0 -0
  109. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/d/t +0 -0
  110. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/test_monitor.js +31 -0
  111. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/test_monitorRootDirectory.js +28 -0
  112. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/test_watchTree.js +20 -0
  113. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/package.json +45 -0
  114. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/test/test.js +243 -0
  115. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/CHANGELOG.md +81 -0
  116. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/LICENSE +177 -0
  117. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/Makefile +11 -0
  118. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/README.md +245 -0
  119. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/binding.gyp +14 -0
  120. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Makefile +355 -0
  121. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/linker.lock +0 -0
  122. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/obj.target/validation/src/validation.o +0 -0
  123. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/obj.target/xor/src/xor.o +0 -0
  124. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/validation.node +0 -0
  125. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/xor.node +0 -0
  126. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/binding.Makefile +6 -0
  127. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/config.gypi +115 -0
  128. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/gyp-mac-tool +512 -0
  129. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/validation.target.mk +154 -0
  130. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/xor.target.mk +154 -0
  131. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/index.js +1 -0
  132. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/install.js +31 -0
  133. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Constants.js +23 -0
  134. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Deprecation.js +38 -0
  135. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Validation.fallback.js +12 -0
  136. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Validation.js +18 -0
  137. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketClient.js +359 -0
  138. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketConnection.js +717 -0
  139. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketFrame.js +282 -0
  140. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketRequest.js +478 -0
  141. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketRouter.js +154 -0
  142. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketRouterRequest.js +59 -0
  143. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketServer.js +216 -0
  144. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/utils.js +7 -0
  145. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/websocket.js +11 -0
  146. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/xor.fallback.js +13 -0
  147. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/xor.js +18 -0
  148. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/package.json +43 -0
  149. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/src/validation.cc +144 -0
  150. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/src/xor.cpp +86 -0
  151. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/vendor/FastBufferList.js +192 -0
  152. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/vendor/node-ctype/LICENSE +18 -0
  153. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/vendor/node-ctype/ctio-faster.js +1126 -0
  154. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/package.json +25 -0
  155. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/browser_websocket.js +63 -0
  156. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/connection_test.js +57 -0
  157. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/logger_mock.js +15 -0
  158. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/session_test.js +194 -0
  159. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/server/flo_test.js +211 -0
  160. data/vendor/assets/javascripts/batman_rails_flo/package.json +15 -0
  161. data/vendor/assets/javascripts/batman_rails_flo/reload_event_handler.js.coffee +56 -0
  162. metadata +232 -0
@@ -0,0 +1,717 @@
1
+ /************************************************************************
2
+ * Copyright 2010-2011 Worlize Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ ***********************************************************************/
16
+
17
+ var crypto = require('crypto');
18
+ var util = require('util');
19
+ var EventEmitter = require('events').EventEmitter;
20
+ var WebSocketFrame = require('./WebSocketFrame');
21
+ var BufferList = require('../vendor/FastBufferList');
22
+ var Constants = require('./Constants');
23
+ var Validation = require('./Validation').Validation;
24
+
25
+ const STATE_OPEN = "open";
26
+ const STATE_CLOSING = "closing";
27
+ const STATE_CLOSED = "closed";
28
+
29
+ function WebSocketConnection(socket, extensions, protocol, maskOutgoingPackets, config) {
30
+ this.config = config;
31
+ this.socket = socket;
32
+ this.protocol = protocol;
33
+ this.extensions = extensions;
34
+ this.remoteAddress = socket.remoteAddress;
35
+ this.closeReasonCode = -1;
36
+ this.closeDescription = null;
37
+
38
+ // We have to mask outgoing packets if we're acting as a WebSocket client.
39
+ this.maskOutgoingPackets = maskOutgoingPackets;
40
+
41
+ // We re-use the same buffers for the mask and frame header for all frames
42
+ // received on each connection to avoid a small memory allocation for each
43
+ // frame.
44
+ this.maskBytes = new Buffer(4);
45
+ this.frameHeader = new Buffer(10);
46
+
47
+ // the BufferList will handle the data streaming in
48
+ this.bufferList = new BufferList();
49
+
50
+ // Prepare for receiving first frame
51
+ this.currentFrame = new WebSocketFrame(this.maskBytes, this.frameHeader, this.config);
52
+ this.fragmentationSize = 0; // data received so far...
53
+ this.frameQueue = [];
54
+
55
+ // Various bits of connection state
56
+ this.connected = true;
57
+ this.state = STATE_OPEN;
58
+ this.waitingForCloseResponse = false;
59
+
60
+ this.closeTimeout = this.config.closeTimeout;
61
+ this.assembleFragments = this.config.assembleFragments;
62
+ this.maxReceivedMessageSize = this.config.maxReceivedMessageSize;
63
+
64
+ // The HTTP Client seems to subscribe to socket error events
65
+ // and re-dispatch them in such a way that doesn't make sense
66
+ // for users of our client, so we want to make sure nobody
67
+ // else is listening for error events on the socket besides us.
68
+ this.socket.removeAllListeners('error');
69
+
70
+ this.socket.on('error', this.handleSocketError.bind(this));
71
+ this.socket.on('data', this.handleSocketData.bind(this));
72
+ this.socket.on('end', this.handleSocketEnd.bind(this));
73
+ this.socket.on('close', this.handleSocketClose.bind(this));
74
+ this.socket.on('drain', this.handleSocketDrain.bind(this));
75
+
76
+ // Disable nagle algorithm?
77
+ this.socket.setNoDelay(this.config.disableNagleAlgorithm);
78
+
79
+ // Make sure there is no socket inactivity timeout
80
+ this.socket.setTimeout(0);
81
+
82
+ this.outgoingFrameQueue = [];
83
+ this.outputPaused = false;
84
+ this.outgoingFrameQueueHandler = this.processOutgoingFrameQueue.bind(this);
85
+ this.bytesWaitingToFlush = 0;
86
+
87
+ this._closeTimerHandler = this.handleCloseTimer.bind(this);
88
+
89
+ if (this.config.keepalive && !this.config.useNativeKeepalive) {
90
+ if (typeof(this.config.keepaliveInterval) !== 'number') {
91
+ throw new Error("keepaliveInterval must be specified and numeric " +
92
+ "if keepalive is true.");
93
+ }
94
+ this._keepaliveTimerHandler = this.handleKeepaliveTimer.bind(this);
95
+ this.setKeepaliveTimer();
96
+
97
+ if (this.config.dropConnectionOnKeepaliveTimeout) {
98
+ if (typeof(this.config.keepaliveGracePeriod) !== 'number') {
99
+ throw new Error("keepaliveGracePeriod must be specified and " +
100
+ "numeric if dropConnectionOnKeepaliveTimeout " +
101
+ "is true.")
102
+ }
103
+ this._gracePeriodTimerHandler = this.handleGracePeriodTimer.bind(this);
104
+ }
105
+ }
106
+ else if (this.config.keepalive && this.config.useNativeKeepalive) {
107
+ if (!('setKeepAlive' in this.socket)) {
108
+ throw new Error("Unable to use native keepalive: unsupported by " +
109
+ "this version of Node.");
110
+ }
111
+ this.socket.setKeepAlive(true, this.config.keepaliveInterval);
112
+ }
113
+ }
114
+
115
+ WebSocketConnection.CLOSE_REASON_NORMAL = 1000;
116
+ WebSocketConnection.CLOSE_REASON_GOING_AWAY = 1001;
117
+ WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR = 1002;
118
+ WebSocketConnection.CLOSE_REASON_UNPROCESSABLE_INPUT = 1003;
119
+ WebSocketConnection.CLOSE_REASON_RESERVED = 1004; // Reserved value. Undefined meaning.
120
+ WebSocketConnection.CLOSE_REASON_NOT_PROVIDED = 1005; // Not to be used on the wire
121
+ WebSocketConnection.CLOSE_REASON_ABNORMAL = 1006; // Not to be used on the wire
122
+ WebSocketConnection.CLOSE_REASON_INVALID_DATA = 1007;
123
+ WebSocketConnection.CLOSE_REASON_POLICY_VIOLATION = 1008;
124
+ WebSocketConnection.CLOSE_REASON_MESSAGE_TOO_BIG = 1009;
125
+ WebSocketConnection.CLOSE_REASON_EXTENSION_REQUIRED = 1010;
126
+ WebSocketConnection.CLOSE_REASON_INTERNAL_SERVER_ERROR = 1011;
127
+ WebSocketConnection.CLOSE_REASON_TLS_HANDSHAKE_FAILED = 1015; // Not to be used on the wire
128
+
129
+ WebSocketConnection.CLOSE_DESCRIPTIONS = {
130
+ 1000: "Normal connection closure",
131
+ 1001: "Remote peer is going away",
132
+ 1002: "Protocol error",
133
+ 1003: "Unprocessable input",
134
+ 1004: "Reserved",
135
+ 1005: "Reason not provided",
136
+ 1006: "Abnormal closure, no further detail available",
137
+ 1007: "Invalid data received",
138
+ 1008: "Policy violation",
139
+ 1009: "Message too big",
140
+ 1010: "Extension requested by client is required",
141
+ 1011: "Internal Server Error",
142
+ 1015: "TLS Handshake Failed"
143
+ };
144
+
145
+ function validateReceivedCloseReason(code) {
146
+ if (code < 1000) {
147
+ // Status codes in the range 0-999 are not used
148
+ return false;
149
+ }
150
+ if (code >= 1000 && code <= 2999) {
151
+ // Codes from 1000 - 2999 are reserved for use by the protocol. Only
152
+ // a few codes are defined, all others are currently illegal.
153
+ return [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011].indexOf(code) !== -1
154
+ }
155
+ if (code >= 3000 && code <= 3999) {
156
+ // Reserved for use by libraries, frameworks, and applications.
157
+ // Should be registered with IANA. Interpretation of these codes is
158
+ // undefined by the WebSocket protocol.
159
+ return true;
160
+ }
161
+ if (code >= 4000 && code <= 4999) {
162
+ // Reserved for private use. Interpretation of these codes is
163
+ // undefined by the WebSocket protocol.
164
+ return true;
165
+ }
166
+ if (code >= 5000) {
167
+ return false;
168
+ }
169
+ }
170
+
171
+ util.inherits(WebSocketConnection, EventEmitter);
172
+
173
+ // set or reset the keepalive timer when data is received.
174
+ WebSocketConnection.prototype.setKeepaliveTimer = function() {
175
+ if (!this.config.keepalive) { return; }
176
+ if (this._keepaliveTimeoutID) {
177
+ clearTimeout(this._keepaliveTimeoutID);
178
+ }
179
+ if (this._gracePeriodTimeoutID) {
180
+ clearTimeout(this._gracePeriodTimeoutID);
181
+ }
182
+ this._keepaliveTimeoutID = setTimeout(this._keepaliveTimerHandler, this.config.keepaliveInterval);
183
+ };
184
+
185
+ // No data has been received within config.keepaliveTimeout ms.
186
+ WebSocketConnection.prototype.handleKeepaliveTimer = function() {
187
+ this._keepaliveTimeoutID = null;
188
+ this.ping();
189
+
190
+ // If we are configured to drop connections if the client doesn't respond
191
+ // then set the grace period timer.
192
+ if (this.config.dropConnectionOnKeepaliveTimeout) {
193
+ this.setGracePeriodTimer();
194
+ }
195
+ else {
196
+ // Otherwise reset the keepalive timer to send the next ping.
197
+ this.setKeepaliveTimer();
198
+ }
199
+ };
200
+
201
+ WebSocketConnection.prototype.setGracePeriodTimer = function() {
202
+ if (this._gracePeriodTimeoutID) {
203
+ clearTimeout(this._gracePeriodTimeoutID);
204
+ }
205
+ this._gracePeriodTimeoutID = setTimeout(this._gracePeriodTimerHandler, this.config.keepaliveGracePeriod);
206
+ };
207
+
208
+ WebSocketConnection.prototype.handleGracePeriodTimer = function() {
209
+ // If this is called, the client has not responded and is assumed dead.
210
+ this._gracePeriodTimeoutID = null;
211
+ this.drop(WebSocketConnection.CLOSE_REASON_ABNORMAL, "Peer not responding.", true);
212
+ };
213
+
214
+ WebSocketConnection.prototype.handleSocketData = function(data) {
215
+ // Reset the keepalive timer when receiving data of any kind.
216
+ this.setKeepaliveTimer();
217
+
218
+ // Add received data to our bufferList, which efficiently holds received
219
+ // data chunks in a linked list of Buffer objects.
220
+ this.bufferList.write(data);
221
+
222
+ // currentFrame.addData returns true if all data necessary to parse
223
+ // the frame was available. It returns false if we are waiting for
224
+ // more data to come in on the wire.
225
+ while (this.connected && this.currentFrame.addData(this.bufferList)) {
226
+
227
+ // Handle possible parsing errors
228
+ if (this.currentFrame.protocolError) {
229
+ // Something bad happened.. get rid of this client.
230
+ this.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR, this.currentFrame.dropReason);
231
+ return;
232
+ }
233
+ else if (this.currentFrame.frameTooLarge) {
234
+ this.drop(WebSocketConnection.CLOSE_REASON_MESSAGE_TOO_BIG, this.currentFrame.dropReason);
235
+ return;
236
+ }
237
+
238
+ // For now since we don't support extensions, all RSV bits are illegal
239
+ if (this.currentFrame.rsv1 || this.currentFrame.rsv2 || this.currentFrame.rsv3) {
240
+ this.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR,
241
+ "Unsupported usage of rsv bits without negotiated extension.");
242
+ }
243
+
244
+ if (!this.assembleFragments) {
245
+ this.emit('frame', this.currentFrame);
246
+ }
247
+ this.processFrame(this.currentFrame);
248
+ this.currentFrame = new WebSocketFrame(this.maskBytes, this.frameHeader, this.config);
249
+ }
250
+ };
251
+
252
+ WebSocketConnection.prototype.handleSocketError = function(error) {
253
+ // console.log((new Date()) + " - Socket Error - Closing Connection: " + error);
254
+ if (this.listeners('error').length > 0) {
255
+ this.emit('error', error);
256
+ }
257
+ this.socket.end();
258
+ };
259
+
260
+ WebSocketConnection.prototype.handleSocketEnd = function() {
261
+ // console.log((new Date()) + " - Socket End");
262
+ this.socket.end();
263
+ this.frameQueue = null;
264
+ this.outgoingFrameQueue = [];
265
+ this.fragmentationSize = 0;
266
+ this.bufferList = null;
267
+ };
268
+
269
+ WebSocketConnection.prototype.handleSocketClose = function(hadError) {
270
+ // console.log((new Date()) + " - Socket Close");
271
+ this.socketHadError = hadError;
272
+ this.connected = false;
273
+ this.state = STATE_CLOSED;
274
+ // If closeReasonCode is still set to -1 at this point then we must
275
+ // not have received a close frame!!
276
+ if (this.closeReasonCode === -1) {
277
+ this.closeReasonCode = WebSocketConnection.CLOSE_REASON_ABNORMAL;
278
+ this.closeDescription = "Connection dropped by remote peer.";
279
+ }
280
+ if (!this.closeEventEmitted) {
281
+ this.closeEventEmitted = true;
282
+ // console.log((new Date()) + " - Emitting WebSocketConnection close event");
283
+ this.emit('close', this.closeReasonCode, this.closeDescription);
284
+ }
285
+ this.clearCloseTimer();
286
+ if (this._keepaliveTimeoutID) {
287
+ clearTimeout(this._keepaliveTimeoutID);
288
+ }
289
+ };
290
+
291
+ WebSocketConnection.prototype.handleSocketDrain = function() {
292
+ this.outputPaused = false;
293
+ this.processOutgoingFrameQueue();
294
+ };
295
+
296
+ WebSocketConnection.prototype.close = function() {
297
+ // console.log((new Date()) + " - Initating clean WebSocket close sequence.");
298
+ if (this.connected) {
299
+ this.closeReasonCode = WebSocketConnection.CLOSE_REASON_NORMAL;
300
+ this.closeDescription = WebSocketConnection.CLOSE_DESCRIPTIONS[this.closeReasonCode];
301
+ this.setCloseTimer();
302
+ this.sendCloseFrame();
303
+ this.state = STATE_CLOSING;
304
+ this.connected = false;
305
+ }
306
+ };
307
+
308
+ WebSocketConnection.prototype.drop = function(reasonCode, description, skipCloseFrame) {
309
+ if (typeof(reasonCode) !== 'number') {
310
+ reasonCode = WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR;
311
+ }
312
+ var logText = "WebSocket: Dropping Connection. Code: " + reasonCode.toString(10);
313
+
314
+ if (typeof(description) !== 'string') {
315
+ // If no description is provided, try to look one up based on the
316
+ // specified reasonCode.
317
+ description = WebSocketConnection.CLOSE_DESCRIPTIONS[reasonCode];
318
+ }
319
+ if (description) {
320
+ logText += (" - " + description);
321
+ }
322
+ // console.error((new Date()) + " " + logText);
323
+
324
+ this.closeReasonCode = reasonCode;
325
+ this.closeDescription = description;
326
+ this.outgoingFrameQueue = [];
327
+ this.frameQueue = [];
328
+ this.fragmentationSize = 0;
329
+ if (!skipCloseFrame) {
330
+ this.sendCloseFrame(reasonCode, description, true);
331
+ }
332
+ this.connected = false;
333
+ this.state = STATE_CLOSED;
334
+ this.closeEventEmitted = true;
335
+ this.emit('close', reasonCode, description);
336
+ this.socket.destroy();
337
+ };
338
+
339
+ WebSocketConnection.prototype.setCloseTimer = function() {
340
+ this.clearCloseTimer();
341
+ // console.log((new Date()) + " - Setting close timer");
342
+ this.waitingForCloseResponse = true;
343
+ this.closeTimer = setTimeout(this._closeTimerHandler, this.closeTimeout);
344
+ };
345
+
346
+ WebSocketConnection.prototype.clearCloseTimer = function() {
347
+ if (this.closeTimer) {
348
+ // console.log((new Date()) + " - Clearing close timer");
349
+ clearTimeout(this.closeTimer);
350
+ this.waitingForCloseResponse = false;
351
+ this.closeTimer = null;
352
+ }
353
+ };
354
+
355
+ WebSocketConnection.prototype.handleCloseTimer = function() {
356
+ this.closeTimer = null;
357
+ if (this.waitingForCloseResponse) {
358
+ // console.log((new Date()) + " - Close response not received from client. Forcing socket end.");
359
+ this.waitingForCloseResponse = false;
360
+ this.socket.end();
361
+ }
362
+ };
363
+
364
+ WebSocketConnection.prototype.processFrame = function(frame) {
365
+ var i;
366
+ var message;
367
+
368
+ // Any non-control opcode besides 0x00 (continuation) received in the
369
+ // middle of a fragmented message is illegal.
370
+ if (this.frameQueue.length !== 0 && (frame.opcode > 0x00 && frame.opcode < 0x08)) {
371
+ this.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR,
372
+ "Illegal frame opcode 0x" + frame.opcode.toString(16) + " " +
373
+ "received in middle of fragmented message.");
374
+ return;
375
+ }
376
+
377
+ switch(frame.opcode) {
378
+ case 0x02: // WebSocketFrame.BINARY_FRAME
379
+ if (this.assembleFragments) {
380
+ if (frame.fin) {
381
+ // Complete single-frame message received
382
+ this.emit('message', {
383
+ type: 'binary',
384
+ binaryData: frame.binaryPayload
385
+ });
386
+ }
387
+ else {
388
+ // beginning of a fragmented message
389
+ this.frameQueue.push(frame);
390
+ this.fragmentationSize = frame.length;
391
+ }
392
+ }
393
+ break;
394
+ case 0x01: // WebSocketFrame.TEXT_FRAME
395
+ if (this.assembleFragments) {
396
+ if (frame.fin) {
397
+ if (!Validation.isValidUTF8(frame.binaryPayload)) {
398
+ this.drop(WebSocketConnection.CLOSE_REASON_INVALID_DATA,
399
+ "Invalid UTF-8 Data Received");
400
+ return;
401
+ }
402
+ // Complete single-frame message received
403
+ this.emit('message', {
404
+ type: 'utf8',
405
+ utf8Data: frame.binaryPayload.toString('utf8')
406
+ });
407
+ }
408
+ else {
409
+ // beginning of a fragmented message
410
+ this.frameQueue.push(frame);
411
+ this.fragmentationSize = frame.length;
412
+ }
413
+ }
414
+ break;
415
+ case 0x00: // WebSocketFrame.CONTINUATION
416
+ if (this.assembleFragments) {
417
+ if (this.frameQueue.length === 0) {
418
+ this.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR,
419
+ "Unexpected Continuation Frame");
420
+ return;
421
+ }
422
+
423
+ this.fragmentationSize += frame.length;
424
+
425
+ if (this.fragmentationSize > this.maxReceivedMessageSize) {
426
+ this.drop(WebSocketConnection.CLOSE_REASON_MESSAGE_TOO_BIG,
427
+ "Maximum message size exceeded.");
428
+ return;
429
+ }
430
+
431
+ this.frameQueue.push(frame);
432
+
433
+ if (frame.fin) {
434
+ // end of fragmented message, so we process the whole
435
+ // message now. We also have to decode the utf-8 data
436
+ // for text frames after combining all the fragments.
437
+ var bytesCopied = 0;
438
+ var binaryPayload = new Buffer(this.fragmentationSize);
439
+ this.frameQueue.forEach(function (currentFrame) {
440
+ currentFrame.binaryPayload.copy(binaryPayload, bytesCopied);
441
+ bytesCopied += currentFrame.binaryPayload.length;
442
+ });
443
+
444
+ switch (this.frameQueue[0].opcode) {
445
+ case 0x02: // WebSocketOpcode.BINARY_FRAME
446
+ this.emit('message', {
447
+ type: 'binary',
448
+ binaryData: binaryPayload
449
+ });
450
+ break;
451
+ case 0x01: // WebSocketOpcode.TEXT_FRAME
452
+ if (!Validation.isValidUTF8(binaryPayload)) {
453
+ this.drop(WebSocketConnection.CLOSE_REASON_INVALID_DATA,
454
+ "Invalid UTF-8 Data Received");
455
+ return;
456
+ }
457
+ this.emit('message', {
458
+ type: 'utf8',
459
+ utf8Data: binaryPayload.toString('utf8')
460
+ });
461
+ break;
462
+ default:
463
+ this.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR,
464
+ "Unexpected first opcode in fragmentation sequence: 0x" + this.frameQueue[0].opcode.toString(16));
465
+ return;
466
+ }
467
+
468
+ this.frameQueue = [];
469
+ this.fragmentationSize = 0;
470
+ }
471
+ }
472
+ break;
473
+ case 0x09: // WebSocketFrame.PING
474
+ this.pong(frame.binaryPayload);
475
+ break;
476
+ case 0x0A: // WebSocketFrame.PONG
477
+ break;
478
+ case 0x08: // WebSocketFrame.CONNECTION_CLOSE
479
+ // console.log((new Date()) + " - Received close frame");
480
+ if (this.waitingForCloseResponse) {
481
+ // Got response to our request to close the connection.
482
+ // Close is complete, so we just hang up.
483
+ // console.log((new Date()) + " - Got close response from peer.");
484
+ this.clearCloseTimer();
485
+ this.waitingForCloseResponse = false;
486
+ this.state = STATE_CLOSED;
487
+ this.socket.end();
488
+ }
489
+ else {
490
+ // Got request from other party to close connection.
491
+ // Send back acknowledgement and then hang up.
492
+ this.state = STATE_CLOSING;
493
+ var respondCloseReasonCode;
494
+
495
+ // Make sure the close reason provided is legal according to
496
+ // the protocol spec. Providing no close status is legal.
497
+ // WebSocketFrame sets closeStatus to -1 by default, so if it
498
+ // is still -1, then no status was provided.
499
+ if (frame.invalidCloseFrameLength) {
500
+ this.closeReasonCode = 1005; // 1005 = No reason provided.
501
+ respondCloseReasonCode = WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR;
502
+ }
503
+ else if (frame.closeStatus === -1 || validateReceivedCloseReason(frame.closeStatus)) {
504
+ this.closeReasonCode = frame.closeStatus;
505
+ respondCloseReasonCode = WebSocketConnection.CLOSE_REASON_NORMAL;
506
+ }
507
+ else {
508
+ this.closeReasonCode = frame.closeStatus;
509
+ respondCloseReasonCode = WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR;
510
+ }
511
+
512
+ // If there is a textual description in the close frame, extract it.
513
+ if (frame.binaryPayload.length > 1) {
514
+ if (!Validation.isValidUTF8(frame.binaryPayload)) {
515
+ this.drop(WebSocketConnection.CLOSE_REASON_INVALID_DATA,
516
+ "Invalid UTF-8 Data Received");
517
+ return;
518
+ }
519
+ this.closeDescription = frame.binaryPayload.toString('utf8');
520
+ }
521
+ else {
522
+ this.closeDescription = WebSocketConnection.CLOSE_DESCRIPTIONS[this.closeReasonCode];
523
+ }
524
+ // console.log((new Date()) + " Remote peer " + this.remoteAddress +
525
+ // " requested disconnect, code: " + this.closeReasonCode + " - " + this.closeDescription +
526
+ // " - close frame payload length: " + frame.length);
527
+ this.sendCloseFrame(respondCloseReasonCode);
528
+ this.socket.end();
529
+ this.connected = false;
530
+ }
531
+ break;
532
+ default:
533
+ this.drop(WebSocketConnection.CLOSE_REASON_PROTOCOL_ERROR,
534
+ "Unrecognized Opcode: 0x" + frame.opcode.toString(16));
535
+ break;
536
+ }
537
+ };
538
+
539
+ WebSocketConnection.prototype.send = function(data, cb) {
540
+ if (Buffer.isBuffer(data)) {
541
+ this.sendBytes(data, cb);
542
+ }
543
+ else if (typeof(data['toString']) === 'function') {
544
+ this.sendUTF(data, cb);
545
+ }
546
+ else {
547
+ throw new Error("Data provided must either be a Node Buffer or implement toString()")
548
+ }
549
+ };
550
+
551
+ WebSocketConnection.prototype.sendUTF = function(data, cb) {
552
+ var frame = new WebSocketFrame(this.maskBytes, this.frameHeader, this.config);
553
+ frame.opcode = 0x01; // WebSocketOpcode.TEXT_FRAME
554
+ frame.binaryPayload = new Buffer(data.toString(), 'utf8');
555
+ this.fragmentAndSend(frame, cb);
556
+ };
557
+
558
+ WebSocketConnection.prototype.sendBytes = function(data, cb) {
559
+ if (!Buffer.isBuffer(data)) {
560
+ throw new Error("You must pass a Node Buffer object to WebSocketConnection.prototype.sendBytes()");
561
+ }
562
+ var frame = new WebSocketFrame(this.maskBytes, this.frameHeader, this.config);
563
+ frame.opcode = 0x02; // WebSocketOpcode.BINARY_FRAME
564
+ frame.binaryPayload = data;
565
+ this.fragmentAndSend(frame, cb);
566
+ };
567
+
568
+ WebSocketConnection.prototype.ping = function(data) {
569
+ var frame = new WebSocketFrame(this.maskBytes, this.frameHeader, this.config);
570
+ frame.opcode = 0x09; // WebSocketOpcode.PING
571
+ frame.fin = true;
572
+ if (data) {
573
+ if (!Buffer.isBuffer(data)) {
574
+ data = new Buffer(data.toString(), 'utf8')
575
+ }
576
+ if (data.length > 125) {
577
+ // console.warn("WebSocket: Data for ping is longer than 125 bytes. Truncating.");
578
+ data = data.slice(0,124);
579
+ }
580
+ frame.binaryPayload = data;
581
+ }
582
+ this.sendFrame(frame);
583
+ };
584
+
585
+ // Pong frames have to echo back the contents of the data portion of the
586
+ // ping frame exactly, byte for byte.
587
+ WebSocketConnection.prototype.pong = function(binaryPayload) {
588
+ var frame = new WebSocketFrame(this.maskBytes, this.frameHeader, this.config);
589
+ frame.opcode = 0x0A; // WebSocketOpcode.PONG
590
+ if (Buffer.isBuffer(binaryPayload) && binaryPayload.length > 125) {
591
+ // console.warn("WebSocket: Data for pong is longer than 125 bytes. Truncating.");
592
+ binaryPayload = binaryPayload.slice(0,124);
593
+ }
594
+ frame.binaryPayload = binaryPayload;
595
+ frame.fin = true;
596
+ this.sendFrame(frame);
597
+ };
598
+
599
+ WebSocketConnection.prototype.fragmentAndSend = function(frame, cb) {
600
+ if (frame.opcode > 0x07) {
601
+ throw new Error("You cannot fragment control frames.");
602
+ }
603
+
604
+ var threshold = this.config.fragmentationThreshold;
605
+ var length = frame.binaryPayload.length;
606
+
607
+ if (this.config.fragmentOutgoingMessages && frame.binaryPayload && length > threshold) {
608
+ var numFragments = Math.ceil(length / threshold);
609
+ var sentFragments = 0;
610
+ var sentCallback = function (err) {
611
+ if (err) {
612
+ if (typeof cb === 'function') {
613
+ // pass only the first error
614
+ cb(err);
615
+ cb = null;
616
+ }
617
+ return;
618
+ }
619
+ ++sentFragments;
620
+ if ((typeof cb === 'function') && (sentFragments === numFragments)) {
621
+ cb();
622
+ }
623
+ }
624
+ for (var i=1; i <= numFragments; i++) {
625
+ var currentFrame = new WebSocketFrame(this.maskBytes, this.frameHeader, this.config);
626
+
627
+ // continuation opcode except for first frame.
628
+ currentFrame.opcode = (i === 1) ? frame.opcode : 0x00;
629
+
630
+ // fin set on last frame only
631
+ currentFrame.fin = (i === numFragments);
632
+
633
+ // length is likely to be shorter on the last fragment
634
+ var currentLength = (i === numFragments) ? length - (threshold * (i-1)) : threshold;
635
+ var sliceStart = threshold * (i-1);
636
+
637
+ // Slice the right portion of the original payload
638
+ currentFrame.binaryPayload = frame.binaryPayload.slice(sliceStart, sliceStart + currentLength);
639
+
640
+ this.sendFrame(currentFrame, sentCallback);
641
+ }
642
+ }
643
+ else {
644
+ frame.fin = true;
645
+ this.sendFrame(frame, cb);
646
+ }
647
+ };
648
+
649
+ WebSocketConnection.prototype.sendCloseFrame = function(reasonCode, reasonText, force) {
650
+ if (typeof(reasonCode) !== 'number') {
651
+ reasonCode = WebSocketConnection.CLOSE_REASON_NORMAL;
652
+ }
653
+ var frame = new WebSocketFrame(this.maskBytes, this.frameHeader, this.config);
654
+ frame.fin = true;
655
+ frame.opcode = 0x08; // WebSocketOpcode.CONNECTION_CLOSE
656
+ frame.closeStatus = reasonCode;
657
+ if (typeof(reasonText) === 'string') {
658
+ frame.binaryPayload = new Buffer(reasonText, 'utf8');
659
+ }
660
+
661
+ this.sendFrame(frame, force);
662
+ };
663
+
664
+ WebSocketConnection.prototype.sendFrame = function(frame, force, cb) {
665
+ if (typeof force === 'function') {
666
+ cb = force;
667
+ force = false;
668
+ }
669
+ frame.mask = this.maskOutgoingPackets;
670
+ var buffer = frame.toBuffer();
671
+ this.outgoingFrameQueue.unshift([buffer, cb]);
672
+ this.bytesWaitingToFlush += buffer.length;
673
+ if (!this.outputPaused || force) {
674
+ this.processOutgoingFrameQueue();
675
+ }
676
+ };
677
+
678
+ WebSocketConnection.prototype.processOutgoingFrameQueue = function() {
679
+ if (this.outputPaused) { return; }
680
+ if (this.outgoingFrameQueue.length > 0) {
681
+ var current = this.outgoingFrameQueue.pop();
682
+ var buffer = current[0];
683
+ var cb = current[1];
684
+ // there is no need to accumulate messages in the queue if connection closed
685
+ // connection will not be restored and messages will never be sent
686
+ // therefore, notify callbacks about it
687
+ if (!this.connected && (typeof cb === 'function')) {
688
+ cb("Connection closed");
689
+ return;
690
+ }
691
+ try {
692
+ var flushed = this.socket.write(buffer, cb);
693
+ }
694
+ catch(e) {
695
+ if (typeof cb === 'function') {
696
+ cb(e.toString());
697
+ }
698
+ if (this.listeners('error').length > 0) {
699
+ this.emit("error", "Error while writing to socket: " + e.toString());
700
+ }
701
+ else {
702
+ if (Constants.DEBUG) {
703
+ console.warn("Error while writing to socket: " + e.toString());
704
+ }
705
+ }
706
+ return;
707
+ }
708
+ this.bytesWaitingToFlush -= buffer.length;
709
+ if (!flushed) {
710
+ this.outputPaused = true;
711
+ return;
712
+ }
713
+ process.nextTick(this.outgoingFrameQueueHandler);
714
+ }
715
+ };
716
+
717
+ module.exports = WebSocketConnection;