websocket-td 0.0.2

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 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: []