bitlove-ruby_bosh 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Pradeep Elankumaran
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,37 @@
1
+ ruby_bosh
2
+ =========
3
+
4
+ The RubyBOSH library handles creating and pre-authenticating BOSH streams inside your Ruby application before passing them off to your template engine.
5
+
6
+ This method allows you to hide authentication details for your users' XMPP accounts.
7
+
8
+ Tested on Rails 2.x with eJabberd 1.2+
9
+
10
+ References
11
+ ==========
12
+ BOSH: http://xmpp.org/extensions/xep-0124.html
13
+ XMPP via BOSH: http://xmpp.org/extensions/xep-0206.html
14
+
15
+ Example
16
+ =======
17
+ In your Ruby app controller (or equivalent):
18
+
19
+ @session_jid, @session_id, @session_random_id =
20
+ RubyBOSH.initialize_session("me@jabber.org", "my_password", "http://localhost:5280/http-bind")
21
+
22
+ In your template, you would then pass these directly to your javascript BOSH connector:
23
+
24
+ var bosh_jid = '<%= @session_jid %>';
25
+ var bosh_sid = '<%= @session_id %>';
26
+ var bosh_rid = '<%= @session_random_id %>';
27
+
28
+ // using Strophe:
29
+ connect.attach(bosh_jid, bosh_sid, bosh_rid, onConnectHandlerFunction);
30
+
31
+ Acknowledgements
32
+ ================
33
+ Jack Moffit
34
+ - thanks for the nice Django example :)
35
+ #=> http://metajack.im/2008/10/03/getting-attached-to-strophe/
36
+
37
+ Copyright (c) 2008 Pradeep Elankumaran. See LICENSE for details.
@@ -0,0 +1,55 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "bitlove-ruby_bosh"
7
+ s.summary = %Q{A BOSH session pre-initializer for Ruby web applications}
8
+ s.email = "pradeep@intridea.com"
9
+ s.homepage = "http://github.com/skyfallsin/ruby_bosh"
10
+ s.description = "An XMPP BOSH session pre-initializer for Ruby web applications"
11
+ s.authors = ["Pradeep Elankumaran"]
12
+
13
+ s.add_dependency("builder")
14
+ s.add_dependency("rest-client")
15
+ s.add_dependency("hpricot")
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
20
+
21
+ require 'rake/rdoctask'
22
+ Rake::RDocTask.new do |rdoc|
23
+ rdoc.rdoc_dir = 'rdoc'
24
+ rdoc.title = 'ruby_bosh'
25
+ rdoc.options << '--line-numbers' << '--inline-source'
26
+ rdoc.rdoc_files.include('README*')
27
+ rdoc.rdoc_files.include('lib/**/*.rb')
28
+ end
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |t|
32
+ t.libs << 'lib' << 'test'
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = false
35
+ end
36
+
37
+ begin
38
+ require 'rcov/rcovtask'
39
+ Rcov::RcovTask.new do |t|
40
+ t.libs << 'test'
41
+ t.test_files = FileList['test/**/*_test.rb']
42
+ t.verbose = true
43
+ end
44
+ rescue LoadError
45
+ puts "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
46
+ end
47
+
48
+ begin
49
+ require 'cucumber/rake/task'
50
+ Cucumber::Rake::Task.new(:features)
51
+ rescue LoadError
52
+ puts "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
53
+ end
54
+
55
+ task :default => :test
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ write basic tests
2
+
@@ -0,0 +1,5 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 7
4
+ :patch: 1
5
+ :build:
@@ -0,0 +1,5 @@
1
+ $:.push(File.join(File.dirname(__FILE__), %w[.. .. rspec]))
2
+
3
+ Autotest.add_discovery do
4
+ "rspec"
5
+ end
@@ -0,0 +1,184 @@
1
+ require 'rest_client'
2
+ require 'builder'
3
+ require 'rexml/document'
4
+ require 'base64'
5
+ require 'hpricot'
6
+
7
+ class RubyBOSH
8
+ BOSH_XMLNS = 'http://jabber.org/protocol/httpbind'
9
+ TLS_XMLNS = 'urn:ietf:params:xml:ns:xmpp-tls'
10
+ SASL_XMLNS = 'urn:ietf:params:xml:ns:xmpp-sasl'
11
+ BIND_XMLNS = 'urn:ietf:params:xml:ns:xmpp-bind'
12
+ SESSION_XMLNS = 'urn:ietf:params:xml:ns:xmpp-session'
13
+ CLIENT_XMLNS = 'jabber:client'
14
+
15
+ class Error < StandardError; end
16
+ class Timeout < RubyBOSH::Error; end
17
+ class AuthFailed < RubyBOSH::Error; end
18
+ class ConnFailed < RubyBOSH::Error; end
19
+
20
+ @@logging = true
21
+ def self.logging=(value)
22
+ @@logging = value
23
+ end
24
+
25
+ attr_accessor :jid, :rid, :sid, :success
26
+ def initialize(jid, pw, service_url, opts={})
27
+ @service_url = service_url
28
+ @jid, @pw = jid, pw
29
+ @host = jid.split("@").last.split("/").first
30
+ @success = false
31
+ @timeout = opts[:timeout] || 3 #seconds
32
+ @headers = {"Content-Type" => "text/xml; charset=utf-8",
33
+ "Accept" => "text/xml"}
34
+ @wait = opts[:wait] || 5
35
+ @hold = opts[:hold] || 3
36
+ @window = opts[:window] || 5
37
+ end
38
+
39
+ def success?
40
+ @success == true
41
+ end
42
+
43
+ def self.initialize_session(*args)
44
+ new(*args).connect
45
+ end
46
+
47
+ def connect
48
+ initialize_bosh_session
49
+ if send_auth_request
50
+ send_restart_request
51
+ request_resource_binding
52
+ @success = send_session_request
53
+ end
54
+
55
+ raise RubyBOSH::AuthFailed, "could not authenticate #{@jid}" unless success?
56
+ @rid += 1 #updates the rid for the next call from the browser
57
+
58
+ [@jid, @sid, @rid]
59
+ end
60
+
61
+ private
62
+ def initialize_bosh_session
63
+ response = deliver(construct_body(:wait => @wait, :to => @host,
64
+ :hold => @hold, :window => @window,
65
+ "xmpp:version" => '1.0'))
66
+ parse(response)
67
+ end
68
+
69
+ def construct_body(params={}, &block)
70
+ @rid ? @rid+=1 : @rid=rand(100000)
71
+
72
+ builder = Builder::XmlMarkup.new
73
+ parameters = {:rid => @rid, :xmlns => BOSH_XMLNS,
74
+ "xmpp:version" => "1.0",
75
+ "xmlns:xmpp" => "urn:xmpp:xbosh"}.merge(params)
76
+
77
+ if block_given?
78
+ builder.body(parameters) {|body| yield(body)}
79
+ else
80
+ builder.body(parameters)
81
+ end
82
+ end
83
+
84
+ def send_auth_request
85
+ request = construct_body(:sid => @sid) do |body|
86
+ auth_string = "#{@jid.split("/").first}\x00#{@jid.split("@").first.strip}\x00#{@pw}"
87
+ body.auth(Base64.encode64(auth_string).gsub(/\s/,''),
88
+ :xmlns => SASL_XMLNS, :mechanism => 'PLAIN')
89
+ end
90
+
91
+ response = deliver(request)
92
+ response.include?("success")
93
+ end
94
+
95
+ def send_restart_request
96
+ request = construct_body(:sid => @sid, "xmpp:restart" => true, "xmlns:xmpp" => 'urn:xmpp:xbosh')
97
+ deliver(request).include?("stream:features")
98
+ end
99
+
100
+ def request_resource_binding
101
+ request = construct_body(:sid => @sid) do |body|
102
+ body.iq(:id => "bind_#{rand(100000)}", :type => "set",
103
+ :xmlns => "jabber:client") do |iq|
104
+ iq.bind(:xmlns => BIND_XMLNS) do |bind|
105
+ bind.resource(@jid.split("/").last)
106
+ end
107
+ end
108
+ end
109
+
110
+ response = deliver(request)
111
+ response.include?("<jid>")
112
+ end
113
+
114
+ def send_session_request
115
+ request = construct_body(:sid => @sid) do |body|
116
+ body.iq(:xmlns => CLIENT_XMLNS, :type => "set",
117
+ :id => "sess_#{rand(100000)}") do |iq|
118
+ iq.session(:xmlns => SESSION_XMLNS)
119
+ end
120
+ end
121
+
122
+ response = deliver(request)
123
+ response.include?("body")
124
+ end
125
+
126
+ def parse(_response)
127
+ doc = Hpricot(_response.to_s)
128
+ doc.search("//body").each do |body|
129
+ @sid = body.attributes["sid"].to_s
130
+ end
131
+ _response
132
+ end
133
+
134
+ begin
135
+ require 'system_timer'
136
+ def deliver(xml)
137
+ SystemTimer.timeout(@timeout) do
138
+ send(xml)
139
+ recv(RestClient.post(@service_url, xml, @headers))
140
+ end
141
+ rescue ::Timeout::Error => e
142
+ raise RubyBOSH::Timeout, e.message
143
+ rescue Errno::ECONNREFUSED => e
144
+ raise RubyBOSH::ConnFailed, "could not connect to #{@host}\n#{e.message}"
145
+ rescue Exception => e
146
+ raise RubyBOSH::Error, e.message
147
+ end
148
+ rescue LoadError
149
+ warn "WARNING: using the built-in Timeout class which is known to have issues when used for opening connections. Install the SystemTimer gem if you want to make sure the Redis client will not hang." unless RUBY_VERSION >= "1.9" || RUBY_PLATFORM =~ /java/
150
+
151
+ require "timeout"
152
+ def deliver(xml)
153
+ ::Timeout.timeout(@timeout) do
154
+ send(xml)
155
+ recv(RestClient.post(@service_url, xml, @headers))
156
+ end
157
+ rescue ::Timeout::Error => e
158
+ raise RubyBOSH::Timeout, e.message
159
+ rescue Errno::ECONNREFUSED => e
160
+ raise RubyBOSH::ConnFailed, "could not connect to #{@host}\n#{e.message}"
161
+ rescue Exception => e
162
+ raise RubyBOSH::Error, e.message
163
+ end
164
+ end
165
+
166
+ def send(msg)
167
+ puts("Ruby-BOSH - SEND\n[#{now}]: #{msg}") if @@logging; msg
168
+ end
169
+
170
+ def recv(msg)
171
+ puts("Ruby-BOSH - RECV\n[#{now}]: #{msg}") if @logging; msg
172
+ end
173
+
174
+ private
175
+ def now
176
+ Time.now.strftime("%a %b %d %H:%M:%S %Y")
177
+ end
178
+ end
179
+
180
+
181
+ if __FILE__ == $0
182
+ p RubyBOSH.initialize_session(ARGV[0], ARGV[1],
183
+ "http://localhost:5280/http-bind")
184
+ end
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ruby_bosh}
8
+ s.version = "0.7.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Pradeep Elankumaran"]
12
+ s.date = %q{2011-05-03}
13
+ s.description = %q{An XMPP BOSH session pre-initializer for Ruby web applications}
14
+ s.email = %q{pradeep@intridea.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README",
18
+ "TODO"
19
+ ]
20
+ s.files = [
21
+ "LICENSE",
22
+ "README",
23
+ "Rakefile",
24
+ "TODO",
25
+ "VERSION.yml",
26
+ "autotest/discover.rb",
27
+ "lib/ruby_bosh.rb",
28
+ "ruby_bosh.gemspec",
29
+ "spec/ruby_bosh_spec.rb",
30
+ "spec/spec_helper.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/skyfallsin/ruby_bosh}
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.6.2}
35
+ s.summary = %q{A BOSH session pre-initializer for Ruby web applications}
36
+ s.test_files = [
37
+ "spec/ruby_bosh_spec.rb",
38
+ "spec/spec_helper.rb"
39
+ ]
40
+
41
+ #stuff running pre rubygems 1.3.2 is probably not on ruby 1.9.x or jruby
42
+ if s.respond_to? :specification_version then
43
+ s.specification_version = 3 #rubygems 1.3.2 2009-01-03
44
+
45
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<builder>, [">= 0"])
47
+ s.add_runtime_dependency(%q<rest-client>, [">= 0"])
48
+ s.add_runtime_dependency(%q<hpricot>, [">= 0"])
49
+ s.add_runtime_dependency(%q<SystemTimer>, [">= 0"]) unless RUBY_VERSION >= "1.9" || RUBY_PLATFORM =~ /java/
50
+ else
51
+ s.add_dependency(%q<builder>, [">= 0"])
52
+ s.add_dependency(%q<rest-client>, [">= 0"])
53
+ s.add_dependency(%q<hpricot>, [">= 0"])
54
+ s.add_dependency(%q<SystemTimer>, [">= 0"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<builder>, [">= 0"])
58
+ s.add_dependency(%q<rest-client>, [">= 0"])
59
+ s.add_dependency(%q<hpricot>, [">= 0"])
60
+ s.add_dependency(%q<SystemTimer>, [">= 0"])
61
+ end
62
+ end
63
+
@@ -0,0 +1,64 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe RubyBOSH do
4
+ before(:each) do
5
+ RubyBOSH.logging = false
6
+ @rbosh = RubyBOSH.new("skyfallsin@localhost", "skyfallsin",
7
+ "http://localhost:5280/http-bind")
8
+ #@rbosh.stub!(:success?).and_return(true)
9
+ #@rbosh.stub!(:initialize_bosh_session).and_return(true)
10
+ @rbosh.stub!(:send_auth_request).and_return(true)
11
+ @rbosh.stub!(:send_restart_request).and_return(true)
12
+ @rbosh.stub!(:request_resource_binding).and_return(true)
13
+ @rbosh.stub!(:send_session_request).and_return(true)
14
+ RestClient.stub!(:post).and_return("<body sid='123456'></body>")
15
+ end
16
+
17
+ it "should set the sid attribute after the session creation request" do
18
+ @rbosh.connect
19
+ @rbosh.sid.should == '123456'
20
+ end
21
+
22
+ it "should update the rid on every call to the BOSH server" do
23
+ @rbosh.rid = 100
24
+ @rbosh.connect
25
+ @rbosh.rid.should > 100
26
+ end
27
+
28
+ it "should return an array with [jid, sid, rid] on success" do
29
+ s = @rbosh.connect
30
+ s.should be_kind_of(Array)
31
+ s.size.should == 3
32
+ s.first.should == 'skyfallsin@localhost'
33
+ s.last.should be_kind_of(Fixnum)
34
+ s[1].should == '123456'
35
+ end
36
+
37
+ describe "Errors" do
38
+ it "should crash with AuthFailed when its not a success?" do
39
+ @rbosh.stub!(:send_session_request).and_return(false)
40
+ lambda { @rbosh.connect }.should raise_error(RubyBOSH::AuthFailed)
41
+ end
42
+
43
+ it "should raise a ConnFailed if a connection could not be made to the XMPP server" do
44
+ RestClient.stub!(:post).and_raise(Errno::ECONNREFUSED)
45
+ lambda { @rbosh.connect }.should raise_error(RubyBOSH::ConnFailed)
46
+ end
47
+
48
+ it "should raise a Timeout::Error if the BOSH call takes forever" do
49
+ SystemTimer.stub!(:timeout).and_raise(::Timeout::Error)
50
+ lambda { @rbosh.connect }.should raise_error(RubyBOSH::Timeout)
51
+ end
52
+
53
+ it "should crash with a generic error on any other problem" do
54
+ [RestClient::ServerBrokeConnection, RestClient::RequestTimeout].each{|err|
55
+ RestClient.stub!(:post).and_raise(err)
56
+ lambda { @rbosh.connect }.should raise_error(RubyBOSH::Error)
57
+ }
58
+ end
59
+
60
+ after(:each) do
61
+ lambda { @rbosh.connect }.should raise_error(RubyBOSH::Error)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', "lib", "ruby_bosh")
3
+ require 'spec'
4
+
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bitlove-ruby_bosh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pradeep Elankumaran
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: builder
16
+ requirement: &70317276255320 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70317276255320
25
+ - !ruby/object:Gem::Dependency
26
+ name: rest-client
27
+ requirement: &70317276253620 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70317276253620
36
+ - !ruby/object:Gem::Dependency
37
+ name: hpricot
38
+ requirement: &70317276252940 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70317276252940
47
+ description: An XMPP BOSH session pre-initializer for Ruby web applications
48
+ email: pradeep@intridea.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README
54
+ - TODO
55
+ files:
56
+ - LICENSE
57
+ - README
58
+ - Rakefile
59
+ - TODO
60
+ - VERSION.yml
61
+ - autotest/discover.rb
62
+ - lib/ruby_bosh.rb
63
+ - ruby_bosh.gemspec
64
+ - spec/ruby_bosh_spec.rb
65
+ - spec/spec_helper.rb
66
+ homepage: http://github.com/skyfallsin/ruby_bosh
67
+ licenses: []
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 1.8.10
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: A BOSH session pre-initializer for Ruby web applications
90
+ test_files: []