batman-rails-flo 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,282 @@
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 ctio = require('../vendor/node-ctype/ctio-faster');
18
+ var xor = require('./xor').xor;
19
+
20
+ const DECODE_HEADER = 1;
21
+ const WAITING_FOR_16_BIT_LENGTH = 2;
22
+ const WAITING_FOR_64_BIT_LENGTH = 3;
23
+ const WAITING_FOR_MASK_KEY = 4;
24
+ const WAITING_FOR_PAYLOAD = 5;
25
+ const COMPLETE = 6;
26
+
27
+ // WebSocketConnection will pass shared buffer objects for maskBytes and
28
+ // frameHeader into the constructor to avoid tons of small memory allocations
29
+ // for each frame we have to parse. This is only used for parsing frames
30
+ // we receive off the wire.
31
+ function WebSocketFrame(maskBytes, frameHeader, config) {
32
+ this.maskBytes = maskBytes;
33
+ this.frameHeader = frameHeader;
34
+ this.config = config;
35
+ this.maxReceivedFrameSize = config.maxReceivedFrameSize;
36
+ this.protocolError = false;
37
+ this.frameTooLarge = false;
38
+ this.invalidCloseFrameLength = false;
39
+ this.parseState = DECODE_HEADER;
40
+ this.closeStatus = -1;
41
+ }
42
+
43
+ WebSocketFrame.prototype.addData = function(bufferList, fragmentationType) {
44
+ var temp;
45
+ if (this.parseState === DECODE_HEADER) {
46
+ if (bufferList.length >= 2) {
47
+ bufferList.joinInto(this.frameHeader, 0, 0, 2);
48
+ bufferList.advance(2);
49
+ var firstByte = this.frameHeader[0];
50
+ var secondByte = this.frameHeader[1];
51
+
52
+ this.fin = Boolean(firstByte & 0x80);
53
+ this.rsv1 = Boolean(firstByte & 0x40);
54
+ this.rsv2 = Boolean(firstByte & 0x20);
55
+ this.rsv3 = Boolean(firstByte & 0x10);
56
+ this.mask = Boolean(secondByte & 0x80);
57
+
58
+ this.opcode = firstByte & 0x0F;
59
+ this.length = secondByte & 0x7F;
60
+
61
+ // Control frame sanity check
62
+ if (this.opcode >= 0x08) {
63
+ if (this.length > 125) {
64
+ this.protocolError = true;
65
+ this.dropReason = "Illegal control frame longer than 125 bytes.";
66
+ return true;
67
+ }
68
+ if (!this.fin) {
69
+ this.protocolError = true;
70
+ this.dropReason = "Control frames must not be fragmented.";
71
+ return true;
72
+ }
73
+ }
74
+
75
+ if (this.length === 126) {
76
+ this.parseState = WAITING_FOR_16_BIT_LENGTH;
77
+ }
78
+ else if (this.length === 127) {
79
+ this.parseState = WAITING_FOR_64_BIT_LENGTH;
80
+ }
81
+ else {
82
+ this.parseState = WAITING_FOR_MASK_KEY;
83
+ }
84
+ }
85
+ }
86
+ if (this.parseState === WAITING_FOR_16_BIT_LENGTH) {
87
+ if (bufferList.length >= 2) {
88
+ bufferList.joinInto(this.frameHeader, 2, 0, 2);
89
+ bufferList.advance(2);
90
+ this.length = ctio.ruint16(this.frameHeader, 'big', 2);
91
+ this.parseState = WAITING_FOR_MASK_KEY;
92
+ }
93
+ }
94
+ else if (this.parseState === WAITING_FOR_64_BIT_LENGTH) {
95
+ if (bufferList.length >= 8) {
96
+ bufferList.joinInto(this.frameHeader, 2, 0, 8);
97
+ bufferList.advance(8);
98
+ var lengthPair = ctio.ruint64(this.frameHeader, 'big', 2);
99
+ if (lengthPair[0] !== 0) {
100
+ this.protocolError = true;
101
+ this.dropReason = "Unsupported 64-bit length frame received";
102
+ return true;
103
+ }
104
+ this.length = lengthPair[1];
105
+ this.parseState = WAITING_FOR_MASK_KEY;
106
+ }
107
+ }
108
+
109
+ if (this.parseState === WAITING_FOR_MASK_KEY) {
110
+ if (this.mask) {
111
+ if (bufferList.length >= 4) {
112
+ bufferList.joinInto(this.maskBytes, 0, 0, 4);
113
+ bufferList.advance(4);
114
+ this.parseState = WAITING_FOR_PAYLOAD;
115
+ }
116
+ }
117
+ else {
118
+ this.parseState = WAITING_FOR_PAYLOAD;
119
+ }
120
+ }
121
+
122
+ if (this.parseState === WAITING_FOR_PAYLOAD) {
123
+ if (this.length > this.maxReceivedFrameSize) {
124
+ this.frameTooLarge = true;
125
+ this.dropReason = "Frame size of " + this.length.toString(10) +
126
+ " bytes exceeds maximum accepted frame size";
127
+ return true;
128
+ }
129
+
130
+ if (this.length === 0) {
131
+ this.binaryPayload = new Buffer(0);
132
+ this.parseState = COMPLETE;
133
+ return true;
134
+ }
135
+ if (bufferList.length >= this.length) {
136
+ this.binaryPayload = bufferList.take(this.length);
137
+ bufferList.advance(this.length);
138
+ if (this.mask) {
139
+ xor(this.binaryPayload, this.maskBytes, 0);
140
+ }
141
+
142
+ if (this.opcode === 0x08) { // WebSocketOpcode.CONNECTION_CLOSE
143
+ if (this.length === 1) {
144
+ // Invalid length for a close frame. Must be zero or at least two.
145
+ this.binaryPayload = new Buffer(0);
146
+ this.invalidCloseFrameLength = true;
147
+ }
148
+ if (this.length >= 2) {
149
+ this.closeStatus = ctio.ruint16(this.binaryPayload, 'big', 0);
150
+ this.binaryPayload = this.binaryPayload.slice(2);
151
+ }
152
+ }
153
+
154
+ this.parseState = COMPLETE;
155
+ return true;
156
+ }
157
+ }
158
+ return false;
159
+ };
160
+
161
+ WebSocketFrame.prototype.throwAwayPayload = function(bufferList) {
162
+ if (bufferList.length >= this.length) {
163
+ bufferList.advance(this.length);
164
+ this.parseState = COMPLETE;
165
+ return true;
166
+ }
167
+ return false;
168
+ };
169
+
170
+ WebSocketFrame.prototype.toBuffer = function(nullMask) {
171
+ var maskKey;
172
+ var headerLength = 2;
173
+ var data;
174
+ var outputPos;
175
+ var firstByte = 0x00;
176
+ var secondByte = 0x00;
177
+
178
+ if (this.fin) {
179
+ firstByte |= 0x80;
180
+ }
181
+ if (this.rsv1) {
182
+ firstByte |= 0x40;
183
+ }
184
+ if (this.rsv2) {
185
+ firstByte |= 0x20;
186
+ }
187
+ if (this.rsv3) {
188
+ firstByte |= 0x10;
189
+ }
190
+ if (this.mask) {
191
+ secondByte |= 0x80;
192
+ }
193
+
194
+ firstByte |= (this.opcode & 0x0F);
195
+
196
+ // the close frame is a special case because the close reason is
197
+ // prepended to the payload data.
198
+ if (this.opcode === 0x08) {
199
+ this.length = 2;
200
+ if (this.binaryPayload) {
201
+ this.length += this.binaryPayload.length;
202
+ }
203
+ data = new Buffer(this.length);
204
+ ctio.wuint16(this.closeStatus, 'big', data, 0);
205
+ if (this.length > 2) {
206
+ this.binaryPayload.copy(data, 2);
207
+ }
208
+ }
209
+ else if (this.binaryPayload) {
210
+ data = this.binaryPayload;
211
+ this.length = data.length;
212
+ }
213
+ else {
214
+ this.length = 0;
215
+ }
216
+
217
+ if (this.length <= 125) {
218
+ // encode the length directly into the two-byte frame header
219
+ secondByte |= (this.length & 0x7F);
220
+ }
221
+ else if (this.length > 125 && this.length <= 0xFFFF) {
222
+ // Use 16-bit length
223
+ secondByte |= 126;
224
+ headerLength += 2;
225
+ }
226
+ else if (this.length > 0xFFFF) {
227
+ // Use 64-bit length
228
+ secondByte |= 127;
229
+ headerLength += 8;
230
+ }
231
+
232
+ var output = new Buffer(this.length + headerLength + (this.mask ? 4 : 0));
233
+
234
+ // write the frame header
235
+ output[0] = firstByte;
236
+ output[1] = secondByte;
237
+
238
+ outputPos = 2;
239
+
240
+ if (this.length > 125 && this.length <= 0xFFFF) {
241
+ // write 16-bit length
242
+ ctio.wuint16(this.length, 'big', output, outputPos);
243
+ outputPos += 2;
244
+ }
245
+ else if (this.length > 0xFFFF) {
246
+ // write 64-bit length
247
+ ctio.wuint64([0x00000000, this.length], 'big', output, outputPos);
248
+ outputPos += 8;
249
+ }
250
+
251
+ if (this.length > 0) {
252
+ if (this.mask) {
253
+ if (!nullMask) {
254
+ // Generate a mask key
255
+ maskKey = parseInt(Math.random()*0xFFFFFFFF);
256
+ }
257
+ else {
258
+ maskKey = 0x00000000;
259
+ }
260
+ ctio.wuint32(maskKey, 'big', this.maskBytes, 0);
261
+
262
+ // write the mask key
263
+ this.maskBytes.copy(output, outputPos);
264
+ outputPos += 4;
265
+
266
+ data.copy(output, outputPos);
267
+ xor(output.slice(outputPos), this.maskBytes, 0);
268
+ }
269
+ else {
270
+ data.copy(output, outputPos);
271
+ }
272
+ }
273
+
274
+ return output;
275
+ };
276
+
277
+ WebSocketFrame.prototype.toString = function() {
278
+ return "Opcode: " + this.opcode + ", fin: " + this.fin + ", length: " + this.length + ", hasPayload: " + Boolean(this.binaryPayload) + ", masked: " + this.mask;
279
+ };
280
+
281
+
282
+ module.exports = WebSocketFrame;
@@ -0,0 +1,478 @@
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 deprecation = require('./Deprecation');
18
+
19
+ var crypto = require('crypto');
20
+ var util = require('util');
21
+ var url = require('url');
22
+ var EventEmitter = require('events').EventEmitter;
23
+ var WebSocketConnection = require('./WebSocketConnection');
24
+ var Constants = require('./Constants');
25
+
26
+ var headerValueSplitRegExp = /,\s*/;
27
+ var headerParamSplitRegExp = /;\s*/;
28
+ var headerSanitizeRegExp = /[\r\n]/g;
29
+ var separators = [
30
+ "(", ")", "<", ">", "@",
31
+ ",", ";", ":", "\\", "\"",
32
+ "/", "[", "]", "?", "=",
33
+ "{", "}", " ", String.fromCharCode(9)
34
+ ];
35
+ var controlChars = [String.fromCharCode(127) /* DEL */];
36
+ for (var i=0; i < 31; i ++) {
37
+ /* US-ASCII Control Characters */
38
+ controlChars.push(String.fromCharCode(i));
39
+ }
40
+
41
+ var cookieNameValidateRegEx = /([\x00-\x20\x22\x28\x29\x2c\x2f\x3a-\x3f\x40\x5b-\x5e\x7b\x7d\x7f])/;
42
+ var cookieValueValidateRegEx = /[^\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]/;
43
+ var cookieValueDQuoteValidateRegEx = /^"[^"]*"$/;
44
+ var controlCharsAndSemicolonRegEx = /[\x00-\x20\x3b]/g;
45
+
46
+ var cookieSeparatorRegEx = /[;,] */;
47
+
48
+ var httpStatusDescriptions = {
49
+ 100: "Continue",
50
+ 101: "Switching Protocols",
51
+ 200: "OK",
52
+ 201: "Created",
53
+ 203: "Non-Authoritative Information",
54
+ 204: "No Content",
55
+ 205: "Reset Content",
56
+ 206: "Partial Content",
57
+ 300: "Multiple Choices",
58
+ 301: "Moved Permanently",
59
+ 302: "Found",
60
+ 303: "See Other",
61
+ 304: "Not Modified",
62
+ 305: "Use Proxy",
63
+ 307: "Temporary Redirect",
64
+ 400: "Bad Request",
65
+ 401: "Unauthorized",
66
+ 402: "Payment Required",
67
+ 403: "Forbidden",
68
+ 404: "Not Found",
69
+ 406: "Not Acceptable",
70
+ 407: "Proxy Authorization Required",
71
+ 408: "Request Timeout",
72
+ 409: "Conflict",
73
+ 410: "Gone",
74
+ 411: "Length Required",
75
+ 412: "Precondition Failed",
76
+ 413: "Request Entity Too Long",
77
+ 414: "Request-URI Too Long",
78
+ 415: "Unsupported Media Type",
79
+ 416: "Requested Range Not Satisfiable",
80
+ 417: "Expectation Failed",
81
+ 426: "Upgrade Required",
82
+ 500: "Internal Server Error",
83
+ 501: "Not Implemented",
84
+ 502: "Bad Gateway",
85
+ 503: "Service Unavailable",
86
+ 504: "Gateway Timeout",
87
+ 505: "HTTP Version Not Supported"
88
+ };
89
+
90
+ function WebSocketRequest(socket, httpRequest, serverConfig) {
91
+ this.socket = socket;
92
+ this.httpRequest = httpRequest;
93
+ this.resource = httpRequest.url;
94
+ this.remoteAddress = socket.remoteAddress;
95
+ this.serverConfig = serverConfig;
96
+ }
97
+
98
+ util.inherits(WebSocketRequest, EventEmitter);
99
+
100
+ WebSocketRequest.prototype.readHandshake = function() {
101
+ var self = this;
102
+ var request = this.httpRequest;
103
+
104
+ // Decode URL
105
+ this.resourceURL = url.parse(this.resource, true);
106
+
107
+ this.host = request.headers['host'];
108
+ if (!this.host) {
109
+ throw new Error("Client must provide a Host header.");
110
+ }
111
+
112
+ this.key = request.headers['sec-websocket-key'];
113
+ if (!this.key) {
114
+ throw new Error("Client must provide a value for Sec-WebSocket-Key.");
115
+ }
116
+
117
+ this.webSocketVersion = parseInt(request.headers['sec-websocket-version'], 10);
118
+ // Deprecated websocketVersion (proper casing...)
119
+ Object.defineProperty(this, "websocketVersion", {
120
+ get: function() {
121
+ deprecation.warn('websocketVersion');
122
+ return this.webSocketVersion;
123
+ }
124
+ });
125
+
126
+ if (!this.webSocketVersion || isNaN(this.webSocketVersion)) {
127
+ throw new Error("Client must provide a value for Sec-WebSocket-Version.");
128
+ }
129
+
130
+ switch (this.webSocketVersion) {
131
+ case 8:
132
+ case 13:
133
+ break;
134
+ default:
135
+ var e = new Error("Unsupported websocket client version: " + this.webSocketVersion +
136
+ "Only versions 8 and 13 are supported.");
137
+ e.httpCode = 426;
138
+ e.headers = {
139
+ "Sec-WebSocket-Version": "13"
140
+ };
141
+ throw e;
142
+ }
143
+
144
+ if (this.webSocketVersion === 13) {
145
+ this.origin = request.headers['origin'];
146
+ }
147
+ else if (this.webSocketVersion === 8) {
148
+ this.origin = request.headers['sec-websocket-origin'];
149
+ }
150
+
151
+ // Protocol is optional.
152
+ var protocolString = request.headers['sec-websocket-protocol'];
153
+ this.protocolFullCaseMap = {};
154
+ this.requestedProtocols = [];
155
+ if (protocolString) {
156
+ var requestedProtocolsFullCase = protocolString.split(headerValueSplitRegExp);
157
+ requestedProtocolsFullCase.forEach(function(protocol) {
158
+ var lcProtocol = protocol.toLocaleLowerCase();
159
+ self.requestedProtocols.push(lcProtocol);
160
+ self.protocolFullCaseMap[lcProtocol] = protocol;
161
+ });
162
+ }
163
+
164
+ if (request.headers['x-forwarded-for']) {
165
+ this.remoteAddress = request.headers['x-forwarded-for'].split(', ')[0];
166
+ }
167
+
168
+ // Extensions are optional.
169
+ var extensionsString = request.headers['sec-websocket-extensions'];
170
+ this.requestedExtensions = this.parseExtensions(extensionsString);
171
+
172
+ // Cookies are optional
173
+ var cookieString = request.headers['cookie'];
174
+ this.cookies = this.parseCookies(cookieString);
175
+ };
176
+
177
+ WebSocketRequest.prototype.parseExtensions = function(extensionsString) {
178
+ if (!extensionsString || extensionsString.length === 0) {
179
+ return [];
180
+ }
181
+ extensions = extensionsString.toLocaleLowerCase().split(headerValueSplitRegExp);
182
+ extensions.forEach(function(extension, index, array) {
183
+ var params = extension.split(headerParamSplitRegExp);
184
+ var extensionName = params[0];
185
+ var extensionParams = params.slice(1);
186
+ extensionParams.forEach(function(rawParam, index, array) {
187
+ var arr = rawParam.split('=');
188
+ var obj = {
189
+ name: arr[0],
190
+ value: arr[1]
191
+ };
192
+ array.splice(index, 1, obj);
193
+ });
194
+ var obj = {
195
+ name: extensionName,
196
+ params: extensionParams
197
+ };
198
+ array.splice(index, 1, obj);
199
+ });
200
+ return extensions;
201
+ };
202
+
203
+ // This function adapted from node-cookie
204
+ // https://github.com/shtylman/node-cookie
205
+ WebSocketRequest.prototype.parseCookies = function(str) {
206
+ // Sanity Check
207
+ if (!str || typeof(str) !== 'string') {
208
+ return [];
209
+ }
210
+
211
+ var cookies = [];
212
+ var pairs = str.split(cookieSeparatorRegEx);
213
+
214
+ pairs.forEach(function(pair) {
215
+ var eq_idx = pair.indexOf('=');
216
+ if (eq_idx === -1) {
217
+ cookies.push({
218
+ name: pair,
219
+ value: null
220
+ });
221
+ return;
222
+ }
223
+
224
+ var key = pair.substr(0, eq_idx).trim();
225
+ var val = pair.substr(++eq_idx, pair.length).trim();
226
+
227
+ // quoted values
228
+ if ('"' == val[0]) {
229
+ val = val.slice(1, -1);
230
+ }
231
+
232
+ cookies.push({
233
+ name: key,
234
+ value: decodeURIComponent(val)
235
+ });
236
+ });
237
+
238
+ return cookies;
239
+ };
240
+
241
+ WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin, cookies) {
242
+ // TODO: Handle extensions
243
+ if (acceptedProtocol) {
244
+ var protocolFullCase = this.protocolFullCaseMap[acceptedProtocol.toLocaleLowerCase()];
245
+ if (typeof(protocolFullCase) === 'undefined') {
246
+ protocolFullCase = acceptedProtocol;
247
+ }
248
+ }
249
+ else {
250
+ protocolFullCase = acceptedProtocol;
251
+ }
252
+ this.protocolFullCaseMap = null;
253
+
254
+ var connection = new WebSocketConnection(this.socket, [], acceptedProtocol, false, this.serverConfig);
255
+
256
+ connection.webSocketVersion = this.webSocketVersion;
257
+ // Deprecated websocketVersion (proper casing...)
258
+ Object.defineProperty(connection, "websocketVersion", {
259
+ get: function() {
260
+ deprecation.warn('websocketVersion');
261
+ return this.webSocketVersion;
262
+ }
263
+ });
264
+ connection.remoteAddress = this.remoteAddress;
265
+
266
+ // Create key validation hash
267
+ var sha1 = crypto.createHash('sha1');
268
+ sha1.update(this.key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
269
+ var acceptKey = sha1.digest('base64');
270
+
271
+ var response = "HTTP/1.1 101 Switching Protocols\r\n" +
272
+ "Upgrade: websocket\r\n" +
273
+ "Connection: Upgrade\r\n" +
274
+ "Sec-WebSocket-Accept: " + acceptKey + "\r\n";
275
+
276
+ if (protocolFullCase) {
277
+ // validate protocol
278
+ for (var i=0; i < protocolFullCase.length; i++) {
279
+ var charCode = protocolFullCase.charCodeAt(i);
280
+ var character = protocolFullCase.charAt(i);
281
+ if (charCode < 0x21 || charCode > 0x7E || separators.indexOf(character) !== -1) {
282
+ this.reject(500);
283
+ throw new Error("Illegal character '" + String.fromCharCode(character) + "' in subprotocol.");
284
+ }
285
+ }
286
+ if (this.requestedProtocols.indexOf(acceptedProtocol) === -1) {
287
+ this.reject(500);
288
+ throw new Error("Specified protocol was not requested by the client.");
289
+ }
290
+
291
+ protocolFullCase = protocolFullCase.replace(headerSanitizeRegExp, '');
292
+ response += "Sec-WebSocket-Protocol: " + protocolFullCase + "\r\n";
293
+ }
294
+ this.requestedProtocols = null;
295
+
296
+ if (allowedOrigin) {
297
+ allowedOrigin = allowedOrigin.replace(headerSanitizeRegExp, '');
298
+ if (this.webSocketVersion === 13) {
299
+ response += "Origin: " + allowedOrigin + "\r\n";
300
+ }
301
+ else if (this.webSocketVersion === 8) {
302
+ response += "Sec-WebSocket-Origin: " + allowedOrigin + "\r\n";
303
+ }
304
+ }
305
+
306
+ if (cookies) {
307
+ if (!Array.isArray(cookies)) {
308
+ this.reject(500);
309
+ throw new Error("Value supplied for 'cookies' argument must be an array.");
310
+ }
311
+ var seenCookies = {};
312
+ cookies.forEach(function(cookie) {
313
+ if (!cookie.name || !cookie.value) {
314
+ this.reject(500);
315
+ throw new Error("Each cookie to set must at least provide a 'name' and 'value'");
316
+ }
317
+
318
+ // Make sure there are no \r\n sequences inserted
319
+ cookie.name = cookie.name.replace(controlCharsAndSemicolonRegEx, '');
320
+ cookie.value = cookie.value.replace(controlCharsAndSemicolonRegEx, '');
321
+
322
+ if (seenCookies[cookie.name]) {
323
+ this.reject(500);
324
+ throw new Error("You may not specify the same cookie name twice.");
325
+ }
326
+ seenCookies[cookie.name] = true;
327
+
328
+ // token (RFC 2616, Section 2.2)
329
+ var invalidChar = cookie.name.match(cookieNameValidateRegEx);
330
+ if (invalidChar) {
331
+ this.reject(500);
332
+ throw new Error("Illegal character " + invalidChar[0] + " in cookie name");
333
+ }
334
+
335
+ // RFC 6265, Section 4.1.1
336
+ // *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) | %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
337
+ if (cookie.value.match(cookieValueDQuoteValidateRegEx)) {
338
+ invalidChar = cookie.value.slice(1, -1).match(cookieValueValidateRegEx);
339
+ } else {
340
+ invalidChar = cookie.value.match(cookieValueValidateRegEx);
341
+ }
342
+ if (invalidChar) {
343
+ this.reject(500);
344
+ throw new Error("Illegal character " + invalidChar[0] + " in cookie value");
345
+ }
346
+
347
+ var cookieParts = [cookie.name + "=" + cookie.value];
348
+
349
+ // RFC 6265, Section 4.1.1
350
+ // "Path=" path-value | <any CHAR except CTLs or ";">
351
+ if(cookie.path){
352
+ invalidChar = cookie.path.match(controlCharsAndSemicolonRegEx);
353
+ if (invalidChar) {
354
+ this.reject(500);
355
+ throw new Error("Illegal character " + invalidChar[0] + " in cookie path");
356
+ }
357
+ cookieParts.push("Path=" + cookie.path);
358
+ }
359
+
360
+ // RFC 6265, Section 4.1.2.3
361
+ // "Domain=" subdomain
362
+ if (cookie.domain) {
363
+ if (typeof(cookie.domain) !== 'string') {
364
+ this.reject(500);
365
+ throw new Error("Domain must be specified and must be a string.");
366
+ }
367
+ var domain = cookie.domain.toLowerCase();
368
+ invalidChar = cookie.domain.match(controlCharsAndSemicolonRegEx);
369
+ if (invalidChar) {
370
+ this.reject(500);
371
+ throw new Error("Illegal character " + invalidChar[0] + " in cookie domain");
372
+ }
373
+ cookieParts.push("Domain=" + cookie.domain.toLowerCase());
374
+ }
375
+
376
+ // RFC 6265, Section 4.1.1
377
+ //"Expires=" sane-cookie-date | Force Date object requirement by using only epoch
378
+ if (cookie.expires) {
379
+ if (!(cookie.expires instanceof Date)){
380
+ this.reject(500);
381
+ throw new Error("Value supplied for cookie 'expires' must be a vaild date object");
382
+ }
383
+ cookieParts.push("Expires=" + cookie.expires.toGMTString());
384
+ }
385
+
386
+ // RFC 6265, Section 4.1.1
387
+ //"Max-Age=" non-zero-digit *DIGIT
388
+ if (cookie.maxage) {
389
+ var maxage = cookie.maxage;
390
+ if (typeof(maxage) === 'string') {
391
+ maxage = parseInt(maxage, 10);
392
+ }
393
+ if (isNaN(maxage) || maxage <= 0 ) {
394
+ this.reject(500);
395
+ throw new Error("Value supplied for cookie 'maxage' must be a non-zero number");
396
+ }
397
+ maxage = Math.round(maxage);
398
+ cookieParts.push("Max-Age=" + maxage.toString(10));
399
+ }
400
+
401
+ // RFC 6265, Section 4.1.1
402
+ //"Secure;"
403
+ if (cookie.secure) {
404
+ if (typeof(cookie.secure) !== "boolean") {
405
+ this.reject(500);
406
+ throw new Error("Value supplied for cookie 'secure' must be of type boolean");
407
+ }
408
+ cookieParts.push("Secure");
409
+ }
410
+
411
+ // RFC 6265, Section 4.1.1
412
+ //"HttpOnly;"
413
+ if (cookie.httponly) {
414
+ if (typeof(cookie.httponly) !== "boolean") {
415
+ this.reject(500);
416
+ throw new Error("Value supplied for cookie 'httponly' must be of type boolean");
417
+ }
418
+ cookieParts.push("HttpOnly");
419
+ }
420
+
421
+ response += ("Set-Cookie: " + cookieParts.join(';') + "\r\n");
422
+ }.bind(this));
423
+ }
424
+
425
+ // TODO: handle negotiated extensions
426
+ // if (negotiatedExtensions) {
427
+ // response += "Sec-WebSocket-Extensions: " + negotiatedExtensions.join(", ") + "\r\n";
428
+ // }
429
+
430
+ response += "\r\n";
431
+ try {
432
+ this.socket.write(response, 'ascii');
433
+ }
434
+ catch(e) {
435
+ if (Constants.DEBUG) {
436
+ console.log("Error Writing to Socket: " + e.toString());
437
+ }
438
+ // Since we have to return a connection object even if the socket is
439
+ // already dead in order not to break the API, we schedule a 'close'
440
+ // event on the connection object to occur immediately.
441
+ process.nextTick(function() {
442
+ // WebSocketConnection.CLOSE_REASON_ABNORMAL = 1006
443
+ // Third param: Skip sending the close frame to a dead socket
444
+ connection.drop(1006, "TCP connection lost before handshake completed.", true);
445
+ });
446
+ }
447
+
448
+ this.emit('requestAccepted', connection);
449
+
450
+ return connection;
451
+ };
452
+
453
+ WebSocketRequest.prototype.reject = function(status, reason, extraHeaders) {
454
+ if (typeof(status) !== 'number') {
455
+ status = 403;
456
+ }
457
+ var response = "HTTP/1.1 " + status + " " + httpStatusDescriptions[status] + "\r\n" +
458
+ "Connection: close\r\n";
459
+ if (reason) {
460
+ reason = reason.replace(headerSanitizeRegExp, '');
461
+ response += "X-WebSocket-Reject-Reason: " + reason + "\r\n";
462
+ }
463
+
464
+ if (extraHeaders) {
465
+ for (var key in extraHeaders) {
466
+ var sanitizedValue = extraHeaders[key].toString().replace(headerSanitizeRegExp, '');
467
+ var sanitizedKey = key.replace(headerSanitizeRegExp, '');
468
+ response += (sanitizedKey + ": " + sanitizedValue + "\r\n");
469
+ }
470
+ }
471
+
472
+ response += "\r\n";
473
+ this.socket.end(response, 'ascii');
474
+
475
+ this.emit('requestRejected', this);
476
+ };
477
+
478
+ module.exports = WebSocketRequest;