bogo-websocket 0.1.0

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: 538a7b638863e49064d58a438df0541bda67566a
4
+ data.tar.gz: bbe48fd88ed074d4e58bbc41390445b69d6e9c8c
5
+ SHA512:
6
+ metadata.gz: 0b460274ce5dd7f7d030e4ed7ab51464e5f6d5847e2047a760797138d0e0cf99b8b8ffeaf330b972c02020fecd51a02400306194be240cd163da77351a062a36
7
+ data.tar.gz: 8199bfc24c40d690ed1ffe19b2528cc27cd7e562234fcf11b1f2f06c52df91c99c2eacc72d9e3490a1d593bd1ad7fb70fa4292379e45b0856335e7a4dea2c5ec
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ # v0.1.0
2
+ * Initial release
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,25 @@
1
+ # Contributing
2
+
3
+ ## Branches
4
+
5
+ ### `master` branch
6
+
7
+ The master branch is the current stable released version.
8
+
9
+ ### `develop` branch
10
+
11
+ The develop branch is the current edge of development.
12
+
13
+ ## Pull requests
14
+
15
+ * https://github.com/spox/bogo-websocket/pulls
16
+
17
+ Please base all pull requests of the `develop` branch. Merges to
18
+ `master` only occur through the `develop` branch. Pull requests
19
+ based on `master` will likely be cherry picked.
20
+
21
+ ## Issues
22
+
23
+ Need to report an issue? Use the github issues:
24
+
25
+ * https://github.com/spox/bogo-websocket/issues
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2015 Chris Roberts
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Bogo Websocket
2
+
3
+ Simple websocket library
4
+
5
+ ## Usage
6
+
7
+ ### Basic Usage
8
+
9
+ ```ruby
10
+ require 'bogo-websocket'
11
+
12
+ socket = Bogo::Websocket::Client.new(
13
+ :destination => 'ws://example.com:8080',
14
+ :path => '/websocket',
15
+ :params => {
16
+ :fubar => true
17
+ },
18
+ :headers => {
19
+ 'X-WOW' => 'custom header'
20
+ },
21
+ :on_connect => proc{
22
+ puts 'Socket Connected'
23
+ },
24
+ :on_disconnect => proc{
25
+ puts 'Socket Disconnected'
26
+ },
27
+ :on_error => proc{|error|
28
+ puts "Error caught: #{error.class} - #{error}"
29
+ },
30
+ :on_message => proc{|msg|
31
+ puts "Received message: #{msg.inspect}"
32
+ }
33
+ )
34
+ socket.write('stuff')
35
+ ```
36
+
37
+ ### SSL Usage
38
+
39
+ ```ruby
40
+ socket = Bogo::Websocket::Client.new(
41
+ :destination => 'wss://example.com:8080',
42
+ ...
43
+ ```
44
+
45
+ #### SSL Usage with Client Key/Certificate
46
+
47
+ ```ruby
48
+ socket = Bogo::Websocket::Client.new(
49
+ :destination => 'wss://example.com:8080',
50
+ :ssl_key => '/local/path/to/key',
51
+ :ssl_certificate => '/local/path/to/cert',
52
+ ...
53
+ ```
54
+
55
+ #### SSL Usage with Custom Context
56
+
57
+ ```ruby
58
+ ssl_ctx = OpenSSL::SSL::SSLContext.new
59
+ ...
60
+ socket = Bogo::Websocket::Client.new(
61
+ :destination => 'wss://example.com:8080',
62
+ :ssl_context => ssl_ctx,
63
+ ...
64
+ ```
65
+
66
+ ## Info
67
+ * Repository: https://github.com/spox/bogo-websocket
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
2
+ require 'bogo-websocket/version'
3
+ Gem::Specification.new do |s|
4
+ s.name = 'bogo-websocket'
5
+ s.version = Bogo::Websocket::VERSION.version
6
+ s.summary = 'Simple websocket libraries'
7
+ s.author = 'Chris Roberts'
8
+ s.email = 'code@chrisroberts.org'
9
+ s.homepage = 'https://github.com/spox/bogo-websocket'
10
+ s.description = 'Simple websocket libraries'
11
+ s.require_path = 'lib'
12
+ s.license = 'Apache 2.0'
13
+ s.add_runtime_dependency 'bogo', '>= 0.1.30', '< 1.0.0'
14
+ s.add_runtime_dependency 'websocket', '~> 1.2.2'
15
+ s.add_development_dependency 'minitest'
16
+ s.files = Dir['lib/**/*'] + %w(bogo-websocket.gemspec README.md CHANGELOG.md CONTRIBUTING.md LICENSE)
17
+ end
@@ -0,0 +1,5 @@
1
+ module Bogo
2
+ module Websocket
3
+ VERSION = Gem::Version.new('0.1.0')
4
+ end
5
+ end
@@ -0,0 +1,184 @@
1
+ require 'openssl'
2
+ require 'websocket'
3
+ require 'bogo-websocket'
4
+
5
+ module Bogo
6
+ module Websocket
7
+
8
+ # Simple websocket client
9
+ class Client
10
+
11
+ include Bogo::Lazy
12
+ include Bogo::Memoization
13
+
14
+ attribute :destination, URI, :required => true, :coerce => lambda{|v| URI.parse(v) }
15
+ attribute :path, String
16
+ attribute :params, Hash, :default => {}
17
+ attribute :headers, Hash, :default => {}
18
+ attribute :ssl_context, OpenSSL::SSL::SSLContext
19
+ attribute :ssl_key, String
20
+ attribute :ssl_certificate, String
21
+
22
+ attribute :on_connect, Proc, :default => proc{}
23
+ attribute :on_disconnect, Proc, :default => proc{}
24
+ attribute :on_error, Proc, :default => proc{|error|}
25
+ attribute :on_message, Proc, :default => proc{|message|}
26
+
27
+ # @return [TCPSocket, OpenSSL::SSL::SSLSocket]
28
+ attr_reader :connection
29
+ # @return [Thread]
30
+ attr_reader :container
31
+ # @return [WebSocket::Frame::Incoming::Client]
32
+ attr_reader :client
33
+ # @return [WebSocket::Handshake::Client]
34
+ attr_reader :handshake
35
+ # @return [TrueClass, FalseClass]
36
+ attr_reader :die
37
+ # @return [IO]
38
+ attr_reader :control_r
39
+ # @return [IO]
40
+ attr_reader :control_w
41
+
42
+ # Create a new websocket client
43
+ #
44
+ # @return [self]
45
+ def initialize(args={})
46
+ load_data(args)
47
+ @control_r, @control_w = IO.pipe
48
+ @die = false
49
+ setup_connection
50
+ perform_handshake
51
+ @lock = Mutex.new
52
+ @container = start!
53
+ end
54
+
55
+ # Write to socket
56
+ #
57
+ # @param line [String]
58
+ def write(line)
59
+ transmit(line, :text)
60
+ end
61
+
62
+ # Close the connection
63
+ def close
64
+ connection.close
65
+ @die = true
66
+ control_w.write 'closed'
67
+ container.join
68
+ end
69
+
70
+ # Start the reader
71
+ def start!
72
+ unless(@container)
73
+ @container = Thread.new do
74
+ until(die || connection.closed?)
75
+ begin
76
+ unless(die || connection.closed?)
77
+ client << connection.read_nonblock(2046)
78
+ if(message = client.next)
79
+ handle_message(message)
80
+ end
81
+ end
82
+ rescue IO::WaitReadable
83
+ unless(die || connection.closed?)
84
+ IO.select([connection, control_r])
85
+ retry
86
+ end
87
+ rescue EOFError
88
+ connection.close
89
+ rescue => error
90
+ on_error.call(error)
91
+ raise
92
+ end
93
+ end
94
+ @connection = nil
95
+ end
96
+ end
97
+ end
98
+
99
+ # Handle an incoming message
100
+ #
101
+ # @param message [WebSocket::Frame::Incoming::Client]
102
+ def handle_message(message)
103
+ case message.type
104
+ when :binary, :text
105
+ on_message.call(message.data)
106
+ when :ping
107
+ transmit(message.data, :pong)
108
+ when :close
109
+ connection.close
110
+ on_disconnect.call
111
+ end
112
+ end
113
+
114
+ # Send data to socket
115
+ #
116
+ # @param data [String]
117
+ # @param type [Symbol]
118
+ def transmit(data, type)
119
+ message = WebSocket::Frame::Outgoing::Client.new(
120
+ :version => handshake.version,
121
+ :data => data,
122
+ :type => type
123
+ )
124
+ result = connection.write message.to_s
125
+ connection.flush
126
+ result
127
+ end
128
+
129
+ # Setup the handshake and perform handshake with remote connection
130
+ #
131
+ # @return [TrueClass]
132
+ def perform_handshake
133
+ port = destination.port || destination.scheme == 'wss' ? 443 : 80
134
+ @handshake = WebSocket::Handshake::Client.new(
135
+ :host => destination.host,
136
+ :port => port,
137
+ :secure => destination.scheme == 'wss',
138
+ :path => path,
139
+ :query => URI.encode_www_form(params),
140
+ :headers => headers
141
+ )
142
+ connection.write handshake.to_s
143
+ reply = ''
144
+ until(handshake.finished?)
145
+ reply << connection.read(1)
146
+ if(reply.index(/\r\n\r\n/m))
147
+ handshake << reply
148
+ reply = ''
149
+ end
150
+ end
151
+ unless(handshake.valid?)
152
+ raise ArgumentError.new 'Invalid handshake. Failed to connect!'
153
+ end
154
+ @client = WebSocket::Frame::Incoming::Client.new(:version => handshake.version)
155
+ true
156
+ end
157
+
158
+ # @return [TCPSocket, OpenSSL::SSL::SSLSocket]
159
+ def setup_connection
160
+ socket = TCPSocket.new(destination.host, destination.port)
161
+ if(destination.scheme == 'wss')
162
+ socket = OpenSSL::SSL::SSLSocket.new(socket, build_ssl_context)
163
+ socket.connect
164
+ end
165
+ @connection = socket
166
+ end
167
+
168
+ # @return [OpenSSL::SSL::SSLContext]
169
+ def build_ssl_context
170
+ if(ssl_context)
171
+ ssl_context
172
+ else
173
+ ctx = OpenSSL::SSL::SSLContext.new
174
+ if(ssl_key || ssl_certificate)
175
+ ctx.cert = OpenSSL::X509::Certificate.new(File.read(ssl_certificate))
176
+ ctx.key = OpenSSL::PKey::RSA.new(File.read(ssl_key))
177
+ end
178
+ ctx
179
+ end
180
+ end
181
+
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,9 @@
1
+ require 'bogo'
2
+
3
+ module Bogo
4
+ module Websocket
5
+ autoload :Client, 'bogo-websocket/websocket'
6
+ end
7
+ end
8
+
9
+ require 'bogo-websocket/version'
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bogo-websocket
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Roberts
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bogo
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.30
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 0.1.30
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: websocket
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 1.2.2
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 1.2.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ description: Simple websocket libraries
62
+ email: code@chrisroberts.org
63
+ executables: []
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - CHANGELOG.md
68
+ - CONTRIBUTING.md
69
+ - LICENSE
70
+ - README.md
71
+ - bogo-websocket.gemspec
72
+ - lib/bogo-websocket.rb
73
+ - lib/bogo-websocket/version.rb
74
+ - lib/bogo-websocket/websocket.rb
75
+ homepage: https://github.com/spox/bogo-websocket
76
+ licenses:
77
+ - Apache 2.0
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.4.8
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Simple websocket libraries
99
+ test_files: []