websocket-td 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 549ad5df2619ccc64b9be924d85d1bb34e7d5601
4
+ data.tar.gz: 8e3a9c62a78aab4095f71e8fa26c0cf922127ee3
5
+ SHA512:
6
+ metadata.gz: d64310c37daa3d8783c82249d76b5933e5d31c191c9b2fb837f0d49edd59a684dd5e87abe8abc0281d23459cb203f1299ae258addace1c8df3775b7dc0d00dbb
7
+ data.tar.gz: b17d9279fa42be544bad826b647c49ac0dd269db0379806aa53b32e42decb045b417ca9c7fcb434998951af81540e3fb1cf7834cc6f4bc6eb5c0df479e4a7d97
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ sftp-config.json
3
+ *.iml
4
+ .idea/
5
+ rdoc/
6
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011-2012 MediaSift Ltd
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
21
+
data/README.md ADDED
@@ -0,0 +1,17 @@
1
+ WebSocket TD
2
+ ========
3
+
4
+ A standards compliant WebSocket client which offers the option of either using 'green' threads or a single threaded evented approach
5
+ Both ws and wss protocols supported.
6
+
7
+ Install Instructions
8
+ --------------------
9
+
10
+ sudo gem install websocket-td
11
+
12
+ Dependencies
13
+ ------------
14
+
15
+
16
+ gem install websocket, multi_json, rdoc, shoulda, test-unit
17
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
data/examples/demo.rb ADDED
@@ -0,0 +1,27 @@
1
+ class Demo
2
+ require '../lib/websocket_td'
3
+
4
+ def initialize
5
+
6
+ client = WebsocketTD::Websocket.new('websocket.datasift.com', '/multi', 'username=zcourts&api_key=91207449dd5b89a4ff38879a770975bf')
7
+ puts 'I continued'
8
+
9
+ client.on_message = lambda { |message| puts "message: #{message.data}" }
10
+
11
+ client.on_open = lambda {
12
+ puts 'opened'
13
+ client.send("{ \"action\":\"subscribe\",\"hash\":\"1f678ba99fbcad0b572011b390cf5124\"}")
14
+ }
15
+ client.on_close = lambda { |m| puts "Closed #{m}" }
16
+ while true
17
+ puts 'subscribing'
18
+ if client.active
19
+ client.send("{ \"action\":\"subscribe\",\"hash\":\"1f678ba99fbcad0b572011b390cf5124\"}")
20
+ end
21
+ sleep(1)
22
+ end
23
+ #if main thread finishes then the process exists...don't let that happen
24
+ client.read_thread.join
25
+ end
26
+ end
27
+ Demo.new
data/lib/errors.rb ADDED
@@ -0,0 +1,8 @@
1
+ class WsError < StandardError
2
+ end
3
+ class ConnectError < WsError
4
+ end
5
+ class WsProtocolError < WsError
6
+ end
7
+ class BadMessageTypeError < WsError
8
+ end
@@ -0,0 +1,192 @@
1
+ require 'websocket'
2
+ require 'socket'
3
+ require '../lib/errors'
4
+
5
+ module WebsocketTD
6
+ class Websocket
7
+ # max length bytes to try to read from a socket per attempt
8
+ @read_buffer = 0
9
+ #true when reading data from a socket
10
+ @active = false
11
+ #the tread currently being used to read data
12
+ @read_thread = nil
13
+ @auto_pong = true
14
+ ##
15
+ # +host+:: Host of request. Required if no :url param was provided.
16
+ # +path+:: Path of request. Should start with '/'. Default: '/'
17
+ # +query+:: Query for request. Should be in format "aaa=bbb&ccc=ddd"
18
+ # +secure+:: Defines protocol to use. If true then wss://, otherwise ws://. This option will not change default port - it should be handled by programmer.
19
+ # +port+:: Port of request. Default: nil
20
+ def initialize(host, path, query, secure = false, port = nil)
21
+ if port == nil
22
+ port = secure ? 443 : 80
23
+ end
24
+
25
+ @handshake = WebSocket::Handshake::Client.new({
26
+ :host => host,
27
+ :port => port,
28
+ :secure => secure,
29
+ :path => path,
30
+ :query => query
31
+ })
32
+
33
+ @read_buffer = 2048
34
+ @auto_pong = true
35
+ @closed = false
36
+ @opened = false
37
+
38
+ @on_open = lambda {}
39
+ @on_close = lambda { |message|}
40
+ @on_ping = lambda { |message|}
41
+ @on_error = lambda { |error|}
42
+ @on_message = lambda { |message|}
43
+
44
+ tcp_socket = TCPSocket.new(host, port)
45
+ if secure
46
+ @socket = OpenSSL::SSL::SSLSocket.new(tcp_socket)
47
+ @socket.connect
48
+ else
49
+ @socket = tcp_socket
50
+ end
51
+
52
+ @socket.write @handshake.to_s
53
+ buf = ''
54
+ headers = ''
55
+ reading = true
56
+
57
+ while reading
58
+ begin
59
+ if @handshake.finished?
60
+ init_messaging
61
+ #don't stop reading until after init_message to guarantee @read_thread != nil for a successful connection
62
+ reading = false
63
+ @opened =true
64
+ fire_on_open
65
+ else
66
+ #do non blocking reads on headers - 1 byte at a time
67
+ buf.concat(@socket.read_nonblock(1))
68
+ #\r\n\r\n i.e. a blank line, separates headers from body
69
+ idx = buf.index(/\r\n\r\n/m)
70
+ if idx != nil
71
+ headers = buf.slice!(0..idx + 8) #+8 to include the blank line separator
72
+ @handshake << headers #parse headers
73
+
74
+ if @handshake.finished? && !@handshake.valid?
75
+ fire_on_error(ConnectError.new('Server responded with an invalid handshake'))
76
+ fire_on_close() #close if handshake is not valid
77
+ end
78
+
79
+ if @handshake.finished?
80
+ @active = true
81
+ buf = '' #clean up
82
+ headers = ''
83
+ end
84
+ end
85
+ end
86
+ rescue IO::WaitReadable
87
+ # ignored
88
+ rescue IO::WaitWritable
89
+ # ignored
90
+ end
91
+ end
92
+ puts headers
93
+ end
94
+
95
+ attr_reader :read_thread, :read_buffer, :socket, :active, :auto_pong
96
+ attr_writer :read_buffer, :auto_pong, :on_ping, :on_error, :on_message # :on_open, :on_close
97
+
98
+ def on_open=(p)
99
+ @on_open = p
100
+ if @opened
101
+ fire_on_open
102
+ end
103
+ end
104
+
105
+ def on_close=(p)
106
+ @on_close = p
107
+ if @closed
108
+ fire_on_close
109
+ end
110
+ end
111
+
112
+ #Use one thread to perform blocking read on the socket
113
+ def init_messaging
114
+ if @read_thread == nil
115
+ @read_thread = Thread.new do
116
+ read_loop
117
+ end
118
+ end
119
+ end
120
+
121
+ def read_loop
122
+ frame = WebSocket::Frame::Incoming::Client.new(:version => @handshake.version)
123
+ while @active
124
+ if @socket.closed?
125
+ @active = false
126
+ fire_on_close
127
+ else
128
+ frame << @socket.readpartial(@read_buffer)
129
+ if (message = frame.next) != nil
130
+ #"text", "binary", "ping", "pong" and "close" (according to websocket/base.rb)
131
+ determine_message_type(message)
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ def determine_message_type(message)
138
+ if message.type == :binary || message.type == :text
139
+ fire_on_message(message)
140
+ elsif message.type == :ping
141
+ if @auto_pong
142
+ send(message.data, :pong)
143
+ end
144
+ fire_on_ping(message)
145
+ elsif message.type == :pong
146
+ fire_on_error(WsProtocolError.new('Invalid type pong received'))
147
+ elsif message.type == :close
148
+ fire_on_close(message)
149
+ else
150
+ fire_on_error(BadMessageTypeError.new("An unknown message type was received #{message.data}"))
151
+ end
152
+ end
153
+
154
+
155
+ def send(data, type = :text)
156
+ frame = WebSocket::Frame::Outgoing::Client.new(:version => @handshake.version, :data => data, :type => type)
157
+ begin
158
+ @socket.write frame #_nonblock
159
+ @socket.flush
160
+ rescue Errno::EPIPE => ce
161
+ fire_on_error(ce)
162
+ fire_on_close
163
+ rescue Exception => e
164
+ fire_on_error(e)
165
+ end
166
+ end
167
+
168
+ def fire_on_ping(message)
169
+ @on_ping.call(message) unless @on_ping == nil
170
+ end
171
+
172
+ def fire_on_message(message)
173
+ @on_message.call(message) unless @on_message == nil
174
+ end
175
+
176
+ def fire_on_open
177
+ @on_open.call() unless @on_open == nil
178
+ end
179
+
180
+ def fire_on_error(error)
181
+ @on_error.call(error) unless @on_error == nil
182
+ end
183
+
184
+ def fire_on_close(message = nil)
185
+ @active = false
186
+ @closed = true
187
+ @on_close.call(message) unless @on_close == nil
188
+ @socket.close
189
+ end
190
+
191
+ end # class
192
+ end # module
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'websocket-td'
3
+ s.version = File.open('VERSION').first
4
+ s.authors = ['Courtney Robinson']
5
+ s.email = ['courtney.robinson@datasift.com']
6
+ s.description = %q{A multi-threaded WebSocket client, i.e. without event machine}
7
+ s.summary = %q{A client which will offer the option of using green threads or a single threaded evented approach}
8
+ s.homepage = 'http://github.com/zcourts/websocket-td'
9
+ s.license = 'BSD'
10
+
11
+ s.platform = Gem::Platform::RUBY
12
+ s.rubygems_version = %q{1.3.6}
13
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.3.6") if s.respond_to? :required_rubygems_version=
14
+
15
+ s.add_runtime_dependency('websocket', '~> 1.1.1')
16
+ s.add_development_dependency('rdoc', '> 0')
17
+ s.add_development_dependency('shoulda', '~> 2.11.3')
18
+ s.add_development_dependency('test-unit', '>= 2.5.5')
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.require_paths = ["lib"]
23
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: websocket-td
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Courtney Robinson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: websocket
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rdoc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>'
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>'
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: shoulda
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 2.11.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.11.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.5.5
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: 2.5.5
69
+ description: A multi-threaded WebSocket client, i.e. without event machine
70
+ email:
71
+ - courtney.robinson@datasift.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE
79
+ - README.md
80
+ - VERSION
81
+ - examples/demo.rb
82
+ - lib/errors.rb
83
+ - lib/websocket_td.rb
84
+ - websocket_td.gemspec
85
+ homepage: http://github.com/zcourts/websocket-td
86
+ licenses:
87
+ - BSD
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '>='
101
+ - !ruby/object:Gem::Version
102
+ version: 1.3.6
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.1.5
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: A client which will offer the option of using green threads or a single threaded
109
+ evented approach
110
+ test_files: []