sonixlabs-em-websocket 0.3.8 → 0.5.1.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 (60) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +69 -0
  3. data/Gemfile +6 -0
  4. data/LICENCE +7 -0
  5. data/README.md +100 -56
  6. data/README.md.BACKUP.14928.md +195 -0
  7. data/README.md.BASE.14928.md +77 -0
  8. data/README.md.LOCAL.14928.md +98 -0
  9. data/README.md.REMOTE.14928.md +142 -0
  10. data/examples/echo.rb +23 -7
  11. data/examples/ping.rb +24 -0
  12. data/examples/test.html +5 -6
  13. data/lib/em-websocket.rb +4 -2
  14. data/lib/em-websocket/close03.rb +3 -0
  15. data/lib/em-websocket/close05.rb +3 -0
  16. data/lib/em-websocket/close06.rb +3 -0
  17. data/lib/em-websocket/close75.rb +2 -1
  18. data/lib/em-websocket/connection.rb +219 -73
  19. data/lib/em-websocket/framing03.rb +6 -11
  20. data/lib/em-websocket/framing05.rb +6 -11
  21. data/lib/em-websocket/framing07.rb +25 -20
  22. data/lib/em-websocket/framing76.rb +6 -15
  23. data/lib/em-websocket/handler.rb +69 -28
  24. data/lib/em-websocket/handler03.rb +0 -1
  25. data/lib/em-websocket/handler05.rb +0 -1
  26. data/lib/em-websocket/handler06.rb +0 -1
  27. data/lib/em-websocket/handler07.rb +0 -1
  28. data/lib/em-websocket/handler08.rb +0 -1
  29. data/lib/em-websocket/handler13.rb +0 -1
  30. data/lib/em-websocket/handler76.rb +2 -0
  31. data/lib/em-websocket/handshake.rb +156 -0
  32. data/lib/em-websocket/handshake04.rb +18 -56
  33. data/lib/em-websocket/handshake75.rb +15 -8
  34. data/lib/em-websocket/handshake76.rb +15 -14
  35. data/lib/em-websocket/masking04.rb +4 -30
  36. data/lib/em-websocket/message_processor_03.rb +13 -4
  37. data/lib/em-websocket/message_processor_06.rb +25 -13
  38. data/lib/em-websocket/version.rb +1 -1
  39. data/lib/em-websocket/websocket.rb +35 -24
  40. data/spec/helper.rb +82 -55
  41. data/spec/integration/common_spec.rb +90 -70
  42. data/spec/integration/draft03_spec.rb +84 -56
  43. data/spec/integration/draft05_spec.rb +14 -12
  44. data/spec/integration/draft06_spec.rb +66 -9
  45. data/spec/integration/draft13_spec.rb +59 -29
  46. data/spec/integration/draft75_spec.rb +46 -40
  47. data/spec/integration/draft76_spec.rb +113 -109
  48. data/spec/integration/gte_03_examples.rb +42 -0
  49. data/spec/integration/shared_examples.rb +174 -0
  50. data/spec/unit/framing_spec.rb +83 -110
  51. data/spec/unit/handshake_spec.rb +216 -0
  52. data/spec/unit/masking_spec.rb +2 -0
  53. metadata +31 -71
  54. data/examples/flash_policy_file_server.rb +0 -21
  55. data/examples/js/FABridge.js +0 -604
  56. data/examples/js/WebSocketMain.swf +0 -0
  57. data/examples/js/swfobject.js +0 -4
  58. data/examples/js/web_socket.js +0 -312
  59. data/lib/em-websocket/handler_factory.rb +0 -107
  60. data/spec/unit/handler_spec.rb +0 -147
@@ -0,0 +1,216 @@
1
+ require 'helper'
2
+
3
+ describe EM::WebSocket::Handshake do
4
+ def handshake(request, secure = false)
5
+ handshake = EM::WebSocket::Handshake.new(secure)
6
+ handshake.receive_data(format_request(request))
7
+ handshake
8
+ end
9
+
10
+ before :each do
11
+ @request = {
12
+ :port => 80,
13
+ :method => "GET",
14
+ :path => "/demo",
15
+ :headers => {
16
+ 'Host' => 'example.com',
17
+ 'Connection' => 'Upgrade',
18
+ 'Sec-WebSocket-Key2' => '12998 5 Y3 1 .P00',
19
+ 'Sec-WebSocket-Protocol' => 'sample',
20
+ 'Upgrade' => 'WebSocket',
21
+ 'Sec-WebSocket-Key1' => '4 @1 46546xW%0l 1 5',
22
+ 'Origin' => 'http://example.com'
23
+ },
24
+ :body => '^n:ds[4U'
25
+ }
26
+ @secure_request = @request.merge(:port => 443)
27
+
28
+ @response = {
29
+ :headers => {
30
+ "Upgrade" => "WebSocket",
31
+ "Connection" => "Upgrade",
32
+ "Sec-WebSocket-Location" => "ws://example.com/demo",
33
+ "Sec-WebSocket-Origin" => "http://example.com",
34
+ "Sec-WebSocket-Protocol" => "sample"
35
+ },
36
+ :body => "8jKS\'y:G*Co,Wxa-"
37
+ }
38
+ @secure_response = @response.merge(:headers => @response[:headers].merge('Sec-WebSocket-Location' => "wss://example.com/demo"))
39
+ end
40
+
41
+ it "should handle good request" do
42
+ handshake(@request).should succeed_with_upgrade(@response)
43
+ end
44
+
45
+ it "should handle good request to secure default port if secure mode is enabled" do
46
+ handshake(@secure_request, true).
47
+ should succeed_with_upgrade(@secure_response)
48
+ end
49
+
50
+ it "should not handle good request to secure default port if secure mode is disabled" do
51
+ handshake(@secure_request, false).
52
+ should_not succeed_with_upgrade(@secure_response)
53
+ end
54
+
55
+ it "should handle good request on nondefault port" do
56
+ @request[:port] = 8081
57
+ @request[:headers]['Host'] = 'example.com:8081'
58
+ @response[:headers]['Sec-WebSocket-Location'] =
59
+ 'ws://example.com:8081/demo'
60
+
61
+ handshake(@request).should succeed_with_upgrade(@response)
62
+ end
63
+
64
+ it "should handle good request to secure nondefault port" do
65
+ @secure_request[:port] = 8081
66
+ @secure_request[:headers]['Host'] = 'example.com:8081'
67
+ @secure_response[:headers]['Sec-WebSocket-Location'] = 'wss://example.com:8081/demo'
68
+
69
+ handshake(@secure_request, true).
70
+ should succeed_with_upgrade(@secure_response)
71
+ end
72
+
73
+ it "should handle good request with no protocol" do
74
+ @request[:headers].delete('Sec-WebSocket-Protocol')
75
+ @response[:headers].delete("Sec-WebSocket-Protocol")
76
+
77
+ handshake(@request).should succeed_with_upgrade(@response)
78
+ end
79
+
80
+ it "should handle extra headers by simply ignoring them" do
81
+ @request[:headers]['EmptyValue'] = ""
82
+ @request[:headers]['AKey'] = "AValue"
83
+
84
+ handshake(@request).should succeed_with_upgrade(@response)
85
+ end
86
+
87
+ it "should raise error on HTTP request" do
88
+ @request[:headers] = {
89
+ 'Host' => 'www.google.com',
90
+ 'User-Agent' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 GTB6 GTBA',
91
+ 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
92
+ 'Accept-Language' => 'en-us,en;q=0.5',
93
+ 'Accept-Encoding' => 'gzip,deflate',
94
+ 'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
95
+ 'Keep-Alive' => '300',
96
+ 'Connection' => 'keep-alive',
97
+ }
98
+
99
+ handshake(@request).should fail_with_error(EM::WebSocket::HandshakeError)
100
+ end
101
+
102
+ it "should raise error on wrong method" do
103
+ @request[:method] = 'POST'
104
+
105
+ handshake(@request).should fail_with_error(EM::WebSocket::HandshakeError)
106
+ end
107
+
108
+ it "should raise error if upgrade header incorrect" do
109
+ @request[:headers]['Upgrade'] = 'NonWebSocket'
110
+
111
+ handshake(@request).should fail_with_error(EM::WebSocket::HandshakeError)
112
+ end
113
+
114
+ it "should raise error if Sec-WebSocket-Protocol is empty" do
115
+ @request[:headers]['Sec-WebSocket-Protocol'] = ''
116
+
117
+ handshake(@request).should fail_with_error(EM::WebSocket::HandshakeError)
118
+ end
119
+
120
+ %w[Sec-WebSocket-Key1 Sec-WebSocket-Key2].each do |header|
121
+ it "should raise error if #{header} has zero spaces" do
122
+ @request[:headers][header] = 'nospaces'
123
+
124
+ handshake(@request).
125
+ should fail_with_error(EM::WebSocket::HandshakeError, 'Websocket Key1 or Key2 does not contain spaces - this is a symptom of a cross-protocol attack')
126
+ end
127
+ end
128
+
129
+ it "should raise error if Sec-WebSocket-Key1 is missing" do
130
+ @request[:headers].delete("Sec-WebSocket-Key1")
131
+
132
+ # The error message isn't correct since key1 is used to heuristically
133
+ # determine the protocol version in use, however this test at least checks
134
+ # that the handshake does correctly fail
135
+ handshake(@request).
136
+ should fail_with_error(EM::WebSocket::HandshakeError, 'Extra bytes after header')
137
+ end
138
+
139
+ it "should raise error if Sec-WebSocket-Key2 is missing" do
140
+ @request[:headers].delete("Sec-WebSocket-Key2")
141
+
142
+ handshake(@request).
143
+ should fail_with_error(EM::WebSocket::HandshakeError, 'WebSocket key1 or key2 is missing')
144
+ end
145
+
146
+ it "should raise error if spaces do not divide numbers in Sec-WebSocket-Key* " do
147
+ @request[:headers]['Sec-WebSocket-Key2'] = '12998 5 Y3 1.P00'
148
+
149
+ handshake(@request).
150
+ should fail_with_error(EM::WebSocket::HandshakeError, 'Invalid Key "12998 5 Y3 1.P00"')
151
+ end
152
+
153
+ it "should raise error if the HTTP header is empty" do
154
+ handshake = EM::WebSocket::Handshake.new(false)
155
+ handshake.receive_data("\r\n\r\nfoobar")
156
+
157
+ handshake.
158
+ should fail_with_error(EM::WebSocket::HandshakeError, 'Invalid HTTP header: Could not parse data entirely (4 != 10)')
159
+ end
160
+
161
+ # This might seems crazy, but very occasionally we saw multiple "Upgrade:
162
+ # WebSocket" headers in the wild. RFC 4.2.1 isn't particularly clear on this
163
+ # point, so for now I have decided not to accept --@mloughran
164
+ it "should raise error on multiple upgrade headers" do
165
+ handshake = EM::WebSocket::Handshake.new(false)
166
+
167
+ # Add a duplicate upgrade header
168
+ headers = format_request(@request)
169
+ upgrade_header = "Upgrade: WebSocket\r\n"
170
+ headers.gsub!(upgrade_header, "#{upgrade_header}#{upgrade_header}")
171
+
172
+ handshake.receive_data(headers)
173
+
174
+ handshake.errback { |e|
175
+ e.class.should == EM::WebSocket::HandshakeError
176
+ e.message.should == 'Invalid upgrade header: ["WebSocket", "WebSocket"]'
177
+ }
178
+ end
179
+
180
+ it "should cope with requests where the header is split" do
181
+ request = format_request(@request)
182
+ incomplete_request = request[0...(request.length / 2)]
183
+ rest = request[(request.length / 2)..-1]
184
+ handshake = EM::WebSocket::Handshake.new(false)
185
+ handshake.receive_data(incomplete_request)
186
+
187
+ handshake.instance_variable_get(:@deferred_status).should == nil
188
+
189
+ # Send the remaining header
190
+ handshake.receive_data(rest)
191
+
192
+ handshake(@request).should succeed_with_upgrade(@response)
193
+ end
194
+
195
+ it "should cope with requests where the third key is split" do
196
+ request = format_request(@request)
197
+ # Removes last two bytes of the third key
198
+ incomplete_request = request[0..-3]
199
+ rest = request[-2..-1]
200
+ handshake = EM::WebSocket::Handshake.new(false)
201
+ handshake.receive_data(incomplete_request)
202
+
203
+ handshake.instance_variable_get(:@deferred_status).should == nil
204
+
205
+ # Send the remaining third key
206
+ handshake.receive_data(rest)
207
+
208
+ handshake(@request).should succeed_with_upgrade(@response)
209
+ end
210
+
211
+ it "should fail if the request URI is invalid" do
212
+ @request[:path] = "/%"
213
+ handshake(@request).should \
214
+ fail_with_error(EM::WebSocket::HandshakeError, 'Invalid request URI: /%')
215
+ end
216
+ end
@@ -23,5 +23,7 @@ describe EM::WebSocket::MaskedString do
23
23
  t.getbyte(4).should == 0x03
24
24
  t.read_mask
25
25
  t.getbyte(4).should == 0x01
26
+ t.unset_mask
27
+ t.getbyte(4).should == 0x03
26
28
  end
27
29
  end
metadata CHANGED
@@ -1,92 +1,52 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sonixlabs-em-websocket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
5
- prerelease:
4
+ version: 0.5.1.1
6
5
  platform: ruby
7
6
  authors:
8
- - Kazuhiro Yamada
7
+ - Ilya Grigorik
8
+ - Martyn Loughran
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-09 00:00:00.000000000Z
12
+ date: 2014-07-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: addressable
16
- requirement: &70155678175840 !ruby/object:Gem::Requirement
17
- none: false
15
+ name: http_parser.rb
16
+ requirement: !ruby/object:Gem::Requirement
18
17
  requirements:
19
- - - ! '>='
18
+ - - "~>"
20
19
  - !ruby/object:Gem::Version
21
- version: 2.1.1
20
+ version: 0.6.0
22
21
  type: :runtime
23
22
  prerelease: false
24
- version_requirements: *70155678175840
25
- - !ruby/object:Gem::Dependency
26
- name: em-spec
27
- requirement: &70155686265480 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ~>
31
- - !ruby/object:Gem::Version
32
- version: 0.2.5
33
- type: :development
34
- prerelease: false
35
- version_requirements: *70155686265480
36
- - !ruby/object:Gem::Dependency
37
- name: em-http-request
38
- requirement: &70155686421520 !ruby/object:Gem::Requirement
39
- none: false
40
- requirements:
41
- - - ~>
42
- - !ruby/object:Gem::Version
43
- version: 0.2.6
44
- type: :development
45
- prerelease: false
46
- version_requirements: *70155686421520
47
- - !ruby/object:Gem::Dependency
48
- name: rspec
49
- requirement: &70155686970220 !ruby/object:Gem::Requirement
50
- none: false
23
+ version_requirements: !ruby/object:Gem::Requirement
51
24
  requirements:
52
- - - ~>
25
+ - - "~>"
53
26
  - !ruby/object:Gem::Version
54
- version: 2.6.0
55
- type: :development
56
- prerelease: false
57
- version_requirements: *70155686970220
58
- - !ruby/object:Gem::Dependency
59
- name: rake
60
- requirement: &70155687189660 !ruby/object:Gem::Requirement
61
- none: false
62
- requirements:
63
- - - ! '>='
64
- - !ruby/object:Gem::Version
65
- version: '0'
66
- type: :development
67
- prerelease: false
68
- version_requirements: *70155687189660
27
+ version: 0.6.0
69
28
  description: EventMachine based WebSocket server
70
29
  email:
71
- - sonixlabs@sonix.asia
72
- - kyamada@sonix.asia
30
+ - ilya@igvita.com
31
+ - me@mloughran.com
73
32
  executables: []
74
33
  extensions: []
75
34
  extra_rdoc_files: []
76
35
  files:
77
- - .gitignore
36
+ - ".gitignore"
78
37
  - CHANGELOG.rdoc
79
38
  - Gemfile
39
+ - LICENCE
80
40
  - README.md
41
+ - README.md.BACKUP.14928.md
42
+ - README.md.BASE.14928.md
43
+ - README.md.LOCAL.14928.md
44
+ - README.md.REMOTE.14928.md
81
45
  - Rakefile
82
46
  - em-websocket.gemspec
83
47
  - examples/echo.rb
84
- - examples/flash_policy_file_server.rb
85
- - examples/js/FABridge.js
86
- - examples/js/WebSocketMain.swf
87
- - examples/js/swfobject.js
88
- - examples/js/web_socket.js
89
48
  - examples/multicast.rb
49
+ - examples/ping.rb
90
50
  - examples/test.html
91
51
  - lib/em-websocket.rb
92
52
  - lib/em-websocket/client_connection.rb
@@ -110,7 +70,7 @@ files:
110
70
  - lib/em-websocket/handler13.rb
111
71
  - lib/em-websocket/handler75.rb
112
72
  - lib/em-websocket/handler76.rb
113
- - lib/em-websocket/handler_factory.rb
73
+ - lib/em-websocket/handshake.rb
114
74
  - lib/em-websocket/handshake04.rb
115
75
  - lib/em-websocket/handshake75.rb
116
76
  - lib/em-websocket/handshake76.rb
@@ -129,34 +89,34 @@ files:
129
89
  - spec/integration/draft13_spec.rb
130
90
  - spec/integration/draft75_spec.rb
131
91
  - spec/integration/draft76_spec.rb
92
+ - spec/integration/gte_03_examples.rb
132
93
  - spec/integration/shared_examples.rb
133
94
  - spec/unit/framing_spec.rb
134
- - spec/unit/handler_spec.rb
95
+ - spec/unit/handshake_spec.rb
135
96
  - spec/unit/masking_spec.rb
136
97
  - spec/unit/message_processor_spec.rb
137
98
  homepage: https://github.com/sonixlabs/em-websocket
138
99
  licenses: []
100
+ metadata: {}
139
101
  post_install_message:
140
102
  rdoc_options: []
141
103
  require_paths:
142
104
  - lib
143
105
  required_ruby_version: !ruby/object:Gem::Requirement
144
- none: false
145
106
  requirements:
146
- - - ! '>='
107
+ - - ">="
147
108
  - !ruby/object:Gem::Version
148
109
  version: '0'
149
110
  required_rubygems_version: !ruby/object:Gem::Requirement
150
- none: false
151
111
  requirements:
152
- - - ! '>='
112
+ - - ">="
153
113
  - !ruby/object:Gem::Version
154
114
  version: '0'
155
115
  requirements: []
156
- rubyforge_project:
157
- rubygems_version: 1.8.6
116
+ rubyforge_project: em-websocket
117
+ rubygems_version: 2.2.2
158
118
  signing_key:
159
- specification_version: 3
119
+ specification_version: 4
160
120
  summary: EventMachine based WebSocket server
161
121
  test_files:
162
122
  - spec/helper.rb
@@ -168,9 +128,9 @@ test_files:
168
128
  - spec/integration/draft13_spec.rb
169
129
  - spec/integration/draft75_spec.rb
170
130
  - spec/integration/draft76_spec.rb
131
+ - spec/integration/gte_03_examples.rb
171
132
  - spec/integration/shared_examples.rb
172
133
  - spec/unit/framing_spec.rb
173
- - spec/unit/handler_spec.rb
134
+ - spec/unit/handshake_spec.rb
174
135
  - spec/unit/masking_spec.rb
175
136
  - spec/unit/message_processor_spec.rb
176
- has_rdoc:
@@ -1,21 +0,0 @@
1
- # Super simple flash policy file server
2
- # See https://github.com/igrigorik/em-websocket/issues/61
3
-
4
- require 'eventmachine'
5
-
6
- module FlashPolicy
7
- def post_init
8
- cross_domain_xml =<<-EOF
9
- <cross-domain-policy>
10
- <allow-access-from domain="*" to-ports="*" />
11
- </cross-domain-policy>
12
- EOF
13
-
14
- send_data cross_domain_xml
15
- close_connection_after_writing
16
- end
17
- end
18
-
19
- EM.run {
20
- EventMachine::start_server '0.0.0.0', 843, FlashPolicy
21
- }
@@ -1,604 +0,0 @@
1
- /*
2
- /*
3
- Copyright 2006 Adobe Systems Incorporated
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
6
- to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
- and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
-
9
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10
-
11
-
12
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
15
- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
-
17
- */
18
-
19
-
20
- /*
21
- * The Bridge class, responsible for navigating AS instances
22
- */
23
- function FABridge(target,bridgeName)
24
- {
25
- this.target = target;
26
- this.remoteTypeCache = {};
27
- this.remoteInstanceCache = {};
28
- this.remoteFunctionCache = {};
29
- this.localFunctionCache = {};
30
- this.bridgeID = FABridge.nextBridgeID++;
31
- this.name = bridgeName;
32
- this.nextLocalFuncID = 0;
33
- FABridge.instances[this.name] = this;
34
- FABridge.idMap[this.bridgeID] = this;
35
-
36
- return this;
37
- }
38
-
39
- // type codes for packed values
40
- FABridge.TYPE_ASINSTANCE = 1;
41
- FABridge.TYPE_ASFUNCTION = 2;
42
-
43
- FABridge.TYPE_JSFUNCTION = 3;
44
- FABridge.TYPE_ANONYMOUS = 4;
45
-
46
- FABridge.initCallbacks = {};
47
- FABridge.userTypes = {};
48
-
49
- FABridge.addToUserTypes = function()
50
- {
51
- for (var i = 0; i < arguments.length; i++)
52
- {
53
- FABridge.userTypes[arguments[i]] = {
54
- 'typeName': arguments[i],
55
- 'enriched': false
56
- };
57
- }
58
- }
59
-
60
- FABridge.argsToArray = function(args)
61
- {
62
- var result = [];
63
- for (var i = 0; i < args.length; i++)
64
- {
65
- result[i] = args[i];
66
- }
67
- return result;
68
- }
69
-
70
- function instanceFactory(objID)
71
- {
72
- this.fb_instance_id = objID;
73
- return this;
74
- }
75
-
76
- function FABridge__invokeJSFunction(args)
77
- {
78
- var funcID = args[0];
79
- var throughArgs = args.concat();//FABridge.argsToArray(arguments);
80
- throughArgs.shift();
81
-
82
- var bridge = FABridge.extractBridgeFromID(funcID);
83
- return bridge.invokeLocalFunction(funcID, throughArgs);
84
- }
85
-
86
- FABridge.addInitializationCallback = function(bridgeName, callback)
87
- {
88
- var inst = FABridge.instances[bridgeName];
89
- if (inst != undefined)
90
- {
91
- callback.call(inst);
92
- return;
93
- }
94
-
95
- var callbackList = FABridge.initCallbacks[bridgeName];
96
- if(callbackList == null)
97
- {
98
- FABridge.initCallbacks[bridgeName] = callbackList = [];
99
- }
100
-
101
- callbackList.push(callback);
102
- }
103
-
104
- // updated for changes to SWFObject2
105
- function FABridge__bridgeInitialized(bridgeName) {
106
- var objects = document.getElementsByTagName("object");
107
- var ol = objects.length;
108
- var activeObjects = [];
109
- if (ol > 0) {
110
- for (var i = 0; i < ol; i++) {
111
- if (typeof objects[i].SetVariable != "undefined") {
112
- activeObjects[activeObjects.length] = objects[i];
113
- }
114
- }
115
- }
116
- var embeds = document.getElementsByTagName("embed");
117
- var el = embeds.length;
118
- var activeEmbeds = [];
119
- if (el > 0) {
120
- for (var j = 0; j < el; j++) {
121
- if (typeof embeds[j].SetVariable != "undefined") {
122
- activeEmbeds[activeEmbeds.length] = embeds[j];
123
- }
124
- }
125
- }
126
- var aol = activeObjects.length;
127
- var ael = activeEmbeds.length;
128
- var searchStr = "bridgeName="+ bridgeName;
129
- if ((aol == 1 && !ael) || (aol == 1 && ael == 1)) {
130
- FABridge.attachBridge(activeObjects[0], bridgeName);
131
- }
132
- else if (ael == 1 && !aol) {
133
- FABridge.attachBridge(activeEmbeds[0], bridgeName);
134
- }
135
- else {
136
- var flash_found = false;
137
- if (aol > 1) {
138
- for (var k = 0; k < aol; k++) {
139
- var params = activeObjects[k].childNodes;
140
- for (var l = 0; l < params.length; l++) {
141
- var param = params[l];
142
- if (param.nodeType == 1 && param.tagName.toLowerCase() == "param" && param["name"].toLowerCase() == "flashvars" && param["value"].indexOf(searchStr) >= 0) {
143
- FABridge.attachBridge(activeObjects[k], bridgeName);
144
- flash_found = true;
145
- break;
146
- }
147
- }
148
- if (flash_found) {
149
- break;
150
- }
151
- }
152
- }
153
- if (!flash_found && ael > 1) {
154
- for (var m = 0; m < ael; m++) {
155
- var flashVars = activeEmbeds[m].attributes.getNamedItem("flashVars").nodeValue;
156
- if (flashVars.indexOf(searchStr) >= 0) {
157
- FABridge.attachBridge(activeEmbeds[m], bridgeName);
158
- break;
159
- }
160
- }
161
- }
162
- }
163
- return true;
164
- }
165
-
166
- // used to track multiple bridge instances, since callbacks from AS are global across the page.
167
-
168
- FABridge.nextBridgeID = 0;
169
- FABridge.instances = {};
170
- FABridge.idMap = {};
171
- FABridge.refCount = 0;
172
-
173
- FABridge.extractBridgeFromID = function(id)
174
- {
175
- var bridgeID = (id >> 16);
176
- return FABridge.idMap[bridgeID];
177
- }
178
-
179
- FABridge.attachBridge = function(instance, bridgeName)
180
- {
181
- var newBridgeInstance = new FABridge(instance, bridgeName);
182
-
183
- FABridge[bridgeName] = newBridgeInstance;
184
-
185
- /* FABridge[bridgeName] = function() {
186
- return newBridgeInstance.root();
187
- }
188
- */
189
- var callbacks = FABridge.initCallbacks[bridgeName];
190
- if (callbacks == null)
191
- {
192
- return;
193
- }
194
- for (var i = 0; i < callbacks.length; i++)
195
- {
196
- callbacks[i].call(newBridgeInstance);
197
- }
198
- delete FABridge.initCallbacks[bridgeName]
199
- }
200
-
201
- // some methods can't be proxied. You can use the explicit get,set, and call methods if necessary.
202
-
203
- FABridge.blockedMethods =
204
- {
205
- toString: true,
206
- get: true,
207
- set: true,
208
- call: true
209
- };
210
-
211
- FABridge.prototype =
212
- {
213
-
214
-
215
- // bootstrapping
216
-
217
- root: function()
218
- {
219
- return this.deserialize(this.target.getRoot());
220
- },
221
- //clears all of the AS objects in the cache maps
222
- releaseASObjects: function()
223
- {
224
- return this.target.releaseASObjects();
225
- },
226
- //clears a specific object in AS from the type maps
227
- releaseNamedASObject: function(value)
228
- {
229
- if(typeof(value) != "object")
230
- {
231
- return false;
232
- }
233
- else
234
- {
235
- var ret = this.target.releaseNamedASObject(value.fb_instance_id);
236
- return ret;
237
- }
238
- },
239
- //create a new AS Object
240
- create: function(className)
241
- {
242
- return this.deserialize(this.target.create(className));
243
- },
244
-
245
-
246
- // utilities
247
-
248
- makeID: function(token)
249
- {
250
- return (this.bridgeID << 16) + token;
251
- },
252
-
253
-
254
- // low level access to the flash object
255
-
256
- //get a named property from an AS object
257
- getPropertyFromAS: function(objRef, propName)
258
- {
259
- if (FABridge.refCount > 0)
260
- {
261
- throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
262
- }
263
- else
264
- {
265
- FABridge.refCount++;
266
- retVal = this.target.getPropFromAS(objRef, propName);
267
- retVal = this.handleError(retVal);
268
- FABridge.refCount--;
269
- return retVal;
270
- }
271
- },
272
- //set a named property on an AS object
273
- setPropertyInAS: function(objRef,propName, value)
274
- {
275
- if (FABridge.refCount > 0)
276
- {
277
- throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
278
- }
279
- else
280
- {
281
- FABridge.refCount++;
282
- retVal = this.target.setPropInAS(objRef,propName, this.serialize(value));
283
- retVal = this.handleError(retVal);
284
- FABridge.refCount--;
285
- return retVal;
286
- }
287
- },
288
-
289
- //call an AS function
290
- callASFunction: function(funcID, args)
291
- {
292
- if (FABridge.refCount > 0)
293
- {
294
- throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
295
- }
296
- else
297
- {
298
- FABridge.refCount++;
299
- retVal = this.target.invokeASFunction(funcID, this.serialize(args));
300
- retVal = this.handleError(retVal);
301
- FABridge.refCount--;
302
- return retVal;
303
- }
304
- },
305
- //call a method on an AS object
306
- callASMethod: function(objID, funcName, args)
307
- {
308
- if (FABridge.refCount > 0)
309
- {
310
- throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
311
- }
312
- else
313
- {
314
- FABridge.refCount++;
315
- args = this.serialize(args);
316
- retVal = this.target.invokeASMethod(objID, funcName, args);
317
- retVal = this.handleError(retVal);
318
- FABridge.refCount--;
319
- return retVal;
320
- }
321
- },
322
-
323
- // responders to remote calls from flash
324
-
325
- //callback from flash that executes a local JS function
326
- //used mostly when setting js functions as callbacks on events
327
- invokeLocalFunction: function(funcID, args)
328
- {
329
- var result;
330
- var func = this.localFunctionCache[funcID];
331
-
332
- if(func != undefined)
333
- {
334
- result = this.serialize(func.apply(null, this.deserialize(args)));
335
- }
336
-
337
- return result;
338
- },
339
-
340
- // Object Types and Proxies
341
-
342
- // accepts an object reference, returns a type object matching the obj reference.
343
- getTypeFromName: function(objTypeName)
344
- {
345
- return this.remoteTypeCache[objTypeName];
346
- },
347
- //create an AS proxy for the given object ID and type
348
- createProxy: function(objID, typeName)
349
- {
350
- var objType = this.getTypeFromName(typeName);
351
- instanceFactory.prototype = objType;
352
- var instance = new instanceFactory(objID);
353
- this.remoteInstanceCache[objID] = instance;
354
- return instance;
355
- },
356
- //return the proxy associated with the given object ID
357
- getProxy: function(objID)
358
- {
359
- return this.remoteInstanceCache[objID];
360
- },
361
-
362
- // accepts a type structure, returns a constructed type
363
- addTypeDataToCache: function(typeData)
364
- {
365
- newType = new ASProxy(this, typeData.name);
366
- var accessors = typeData.accessors;
367
- for (var i = 0; i < accessors.length; i++)
368
- {
369
- this.addPropertyToType(newType, accessors[i]);
370
- }
371
-
372
- var methods = typeData.methods;
373
- for (var i = 0; i < methods.length; i++)
374
- {
375
- if (FABridge.blockedMethods[methods[i]] == undefined)
376
- {
377
- this.addMethodToType(newType, methods[i]);
378
- }
379
- }
380
-
381
-
382
- this.remoteTypeCache[newType.typeName] = newType;
383
- return newType;
384
- },
385
-
386
- //add a property to a typename; used to define the properties that can be called on an AS proxied object
387
- addPropertyToType: function(ty, propName)
388
- {
389
- var c = propName.charAt(0);
390
- var setterName;
391
- var getterName;
392
- if(c >= "a" && c <= "z")
393
- {
394
- getterName = "get" + c.toUpperCase() + propName.substr(1);
395
- setterName = "set" + c.toUpperCase() + propName.substr(1);
396
- }
397
- else
398
- {
399
- getterName = "get" + propName;
400
- setterName = "set" + propName;
401
- }
402
- ty[setterName] = function(val)
403
- {
404
- this.bridge.setPropertyInAS(this.fb_instance_id, propName, val);
405
- }
406
- ty[getterName] = function()
407
- {
408
- return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName));
409
- }
410
- },
411
-
412
- //add a method to a typename; used to define the methods that can be callefd on an AS proxied object
413
- addMethodToType: function(ty, methodName)
414
- {
415
- ty[methodName] = function()
416
- {
417
- return this.bridge.deserialize(this.bridge.callASMethod(this.fb_instance_id, methodName, FABridge.argsToArray(arguments)));
418
- }
419
- },
420
-
421
- // Function Proxies
422
-
423
- //returns the AS proxy for the specified function ID
424
- getFunctionProxy: function(funcID)
425
- {
426
- var bridge = this;
427
- if (this.remoteFunctionCache[funcID] == null)
428
- {
429
- this.remoteFunctionCache[funcID] = function()
430
- {
431
- bridge.callASFunction(funcID, FABridge.argsToArray(arguments));
432
- }
433
- }
434
- return this.remoteFunctionCache[funcID];
435
- },
436
-
437
- //reutrns the ID of the given function; if it doesnt exist it is created and added to the local cache
438
- getFunctionID: function(func)
439
- {
440
- if (func.__bridge_id__ == undefined)
441
- {
442
- func.__bridge_id__ = this.makeID(this.nextLocalFuncID++);
443
- this.localFunctionCache[func.__bridge_id__] = func;
444
- }
445
- return func.__bridge_id__;
446
- },
447
-
448
- // serialization / deserialization
449
-
450
- serialize: function(value)
451
- {
452
- var result = {};
453
-
454
- var t = typeof(value);
455
- //primitives are kept as such
456
- if (t == "number" || t == "string" || t == "boolean" || t == null || t == undefined)
457
- {
458
- result = value;
459
- }
460
- else if (value instanceof Array)
461
- {
462
- //arrays are serializesd recursively
463
- result = [];
464
- for (var i = 0; i < value.length; i++)
465
- {
466
- result[i] = this.serialize(value[i]);
467
- }
468
- }
469
- else if (t == "function")
470
- {
471
- //js functions are assigned an ID and stored in the local cache
472
- result.type = FABridge.TYPE_JSFUNCTION;
473
- result.value = this.getFunctionID(value);
474
- }
475
- else if (value instanceof ASProxy)
476
- {
477
- result.type = FABridge.TYPE_ASINSTANCE;
478
- result.value = value.fb_instance_id;
479
- }
480
- else
481
- {
482
- result.type = FABridge.TYPE_ANONYMOUS;
483
- result.value = value;
484
- }
485
-
486
- return result;
487
- },
488
-
489
- //on deserialization we always check the return for the specific error code that is used to marshall NPE's into JS errors
490
- // the unpacking is done by returning the value on each pachet for objects/arrays
491
- deserialize: function(packedValue)
492
- {
493
-
494
- var result;
495
-
496
- var t = typeof(packedValue);
497
- if (t == "number" || t == "string" || t == "boolean" || packedValue == null || packedValue == undefined)
498
- {
499
- result = this.handleError(packedValue);
500
- }
501
- else if (packedValue instanceof Array)
502
- {
503
- result = [];
504
- for (var i = 0; i < packedValue.length; i++)
505
- {
506
- result[i] = this.deserialize(packedValue[i]);
507
- }
508
- }
509
- else if (t == "object")
510
- {
511
- for(var i = 0; i < packedValue.newTypes.length; i++)
512
- {
513
- this.addTypeDataToCache(packedValue.newTypes[i]);
514
- }
515
- for (var aRefID in packedValue.newRefs)
516
- {
517
- this.createProxy(aRefID, packedValue.newRefs[aRefID]);
518
- }
519
- if (packedValue.type == FABridge.TYPE_PRIMITIVE)
520
- {
521
- result = packedValue.value;
522
- }
523
- else if (packedValue.type == FABridge.TYPE_ASFUNCTION)
524
- {
525
- result = this.getFunctionProxy(packedValue.value);
526
- }
527
- else if (packedValue.type == FABridge.TYPE_ASINSTANCE)
528
- {
529
- result = this.getProxy(packedValue.value);
530
- }
531
- else if (packedValue.type == FABridge.TYPE_ANONYMOUS)
532
- {
533
- result = packedValue.value;
534
- }
535
- }
536
- return result;
537
- },
538
- //increases the reference count for the given object
539
- addRef: function(obj)
540
- {
541
- this.target.incRef(obj.fb_instance_id);
542
- },
543
- //decrease the reference count for the given object and release it if needed
544
- release:function(obj)
545
- {
546
- this.target.releaseRef(obj.fb_instance_id);
547
- },
548
-
549
- // check the given value for the components of the hard-coded error code : __FLASHERROR
550
- // used to marshall NPE's into flash
551
-
552
- handleError: function(value)
553
- {
554
- if (typeof(value)=="string" && value.indexOf("__FLASHERROR")==0)
555
- {
556
- var myErrorMessage = value.split("||");
557
- if(FABridge.refCount > 0 )
558
- {
559
- FABridge.refCount--;
560
- }
561
- throw new Error(myErrorMessage[1]);
562
- return value;
563
- }
564
- else
565
- {
566
- return value;
567
- }
568
- }
569
- };
570
-
571
- // The root ASProxy class that facades a flash object
572
-
573
- ASProxy = function(bridge, typeName)
574
- {
575
- this.bridge = bridge;
576
- this.typeName = typeName;
577
- return this;
578
- };
579
- //methods available on each ASProxy object
580
- ASProxy.prototype =
581
- {
582
- get: function(propName)
583
- {
584
- return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName));
585
- },
586
-
587
- set: function(propName, value)
588
- {
589
- this.bridge.setPropertyInAS(this.fb_instance_id, propName, value);
590
- },
591
-
592
- call: function(funcName, args)
593
- {
594
- this.bridge.callASMethod(this.fb_instance_id, funcName, args);
595
- },
596
-
597
- addRef: function() {
598
- this.bridge.addRef(this);
599
- },
600
-
601
- release: function() {
602
- this.bridge.release(this);
603
- }
604
- };