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.
- checksums.yaml +7 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/lib/batman-rails-flo.rb +5 -0
- data/lib/batman_rails_flo/railtie.rb +9 -0
- data/lib/batman_rails_flo/tasks.rake +14 -0
- data/lib/batman_rails_flo/version.rb +3 -0
- data/vendor/assets/javascripts/batman_rails_flo/flo_server.js +67 -0
- data/vendor/assets/javascripts/batman_rails_flo/live_reload.js.coffee +129 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/CNAME +1 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/CONTRIBUTING.md +9 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/LICENSE +22 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/README +50 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/bin/cake +7 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/bin/coffee +7 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/browser.js +134 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/cake.js +112 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/coffee-script.js +335 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/command.js +569 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/grammar.js +631 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/helpers.js +252 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/index.js +11 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/lexer.js +926 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/nodes.js +3158 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/optparse.js +139 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/parser.js +724 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/register.js +66 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/repl.js +163 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/rewriter.js +475 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/scope.js +146 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/sourcemap.js +161 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/LICENSE +21 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/examples/pow.js +6 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/index.js +82 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/package.json +34 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/readme.markdown +63 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/chmod.js +38 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/clobber.js +37 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/mkdirp.js +28 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/perm.js +32 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/perm_sync.js +39 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/race.js +41 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/rel.js +32 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/return.js +25 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/return_sync.js +24 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/root.js +18 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/sync.js +32 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/umask.js +28 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/umask_sync.js +32 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/package.json +50 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/register.js +1 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/LICENSE +30 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/PATENTS +23 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/README.md +124 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/bin/flo +26 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/configure/configure.html +57 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/configure/configure.js +205 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/configure/style.css +334 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/connection.js +235 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/devtools.html +12 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/index.js +323 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/logger.js +42 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/logo.png +0 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/manifest.json +13 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/session.js +305 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/index.js +10 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/lib/flo.js +226 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/lib/server.js +94 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/README.md +66 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/index.js +437 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/LICENSE +23 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/README.md +218 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/minimatch.js +1055 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS +14 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/LICENSE +23 -0
- 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
- 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
- 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
- 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
- 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
- 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
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/LICENSE +27 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/README.md +53 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/bench.js +283 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/package.json +42 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/sigmund.js +39 -0
- 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
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/package.json +40 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/basic.js +399 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/brace-expand.js +33 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/caching.js +14 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/defaults.js +274 -0
- 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
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/lib/walker.js +111 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/license +13 -0
- 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
- 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
- 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
- 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
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/package.json +34 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/readme.md +77 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/package.json +36 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/readme.md +52 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/LICENSE +55 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/main.js +128 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/package.json +34 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/readme.mkd +72 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/d/d/t +0 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/d/t +0 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/test_monitor.js +31 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/test_monitorRootDirectory.js +28 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/test_watchTree.js +20 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/package.json +45 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/test/test.js +243 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/CHANGELOG.md +81 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/LICENSE +177 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/Makefile +11 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/README.md +245 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/binding.gyp +14 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Makefile +355 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/linker.lock +0 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/obj.target/validation/src/validation.o +0 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/obj.target/xor/src/xor.o +0 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/validation.node +0 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/xor.node +0 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/binding.Makefile +6 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/config.gypi +115 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/gyp-mac-tool +512 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/validation.target.mk +154 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/xor.target.mk +154 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/index.js +1 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/install.js +31 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Constants.js +23 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Deprecation.js +38 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Validation.fallback.js +12 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Validation.js +18 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketClient.js +359 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketConnection.js +717 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketFrame.js +282 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketRequest.js +478 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketRouter.js +154 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketRouterRequest.js +59 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketServer.js +216 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/utils.js +7 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/websocket.js +11 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/xor.fallback.js +13 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/xor.js +18 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/package.json +43 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/src/validation.cc +144 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/src/xor.cpp +86 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/vendor/FastBufferList.js +192 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/vendor/node-ctype/LICENSE +18 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/vendor/node-ctype/ctio-faster.js +1126 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/package.json +25 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/browser_websocket.js +63 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/connection_test.js +57 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/logger_mock.js +15 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/session_test.js +194 -0
- data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/server/flo_test.js +211 -0
- data/vendor/assets/javascripts/batman_rails_flo/package.json +15 -0
- data/vendor/assets/javascripts/batman_rails_flo/reload_event_handler.js.coffee +56 -0
- 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;
|