bogo-websocket 0.1.0

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