websocket-eventmachine-base 1.0.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.
- data/.gitignore +2 -0
- data/CHANGELOG.md +5 -0
- data/README.md +11 -0
- data/Rakefile +2 -0
- data/lib/websocket/eventmachine/base/version.rb +7 -0
- data/lib/websocket/eventmachine/base.rb +191 -0
- data/lib/websocket-eventmachine-base.rb +1 -0
- data/websocket-eventmachine-base.gemspec +23 -0
- metadata +101 -0
data/.gitignore
ADDED
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# WebSocket Base for Ruby client and server
|
2
|
+
|
3
|
+
WebSocket-EventMachine-Base is base gem for [WebSocket-EventMachine-Client](http://github.com/imanel/websocket-eventmachine-client) and [WebSocket-EventMachine-Server](http://github.com/imanel/websocket-eventmachine-server)
|
4
|
+
|
5
|
+
This gem should not be used directly - please refer to client or server gem for full specification.
|
6
|
+
|
7
|
+
- [Docs](http://rdoc.info/github/imanel/websocket-eventmachine-base/master/frames)
|
8
|
+
|
9
|
+
## License
|
10
|
+
|
11
|
+
The MIT License - Copyright (c) 2012 Bernard Potocki
|
data/Rakefile
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'websocket'
|
2
|
+
require 'eventmachine'
|
3
|
+
|
4
|
+
module WebSocket
|
5
|
+
module EventMachine
|
6
|
+
|
7
|
+
# WebSocket Base for Client and Server (using EventMachine)
|
8
|
+
class Base < ::EventMachine::Connection
|
9
|
+
|
10
|
+
###########
|
11
|
+
### API ###
|
12
|
+
###########
|
13
|
+
|
14
|
+
# Called when connection is opened.
|
15
|
+
# No parameters are passed to block
|
16
|
+
def onopen(&blk); @onopen = blk; end
|
17
|
+
|
18
|
+
# Called when connection is closed.
|
19
|
+
# No parameters are passed to block
|
20
|
+
def onclose(&blk); @onclose = blk; end
|
21
|
+
|
22
|
+
# Called when error occurs.
|
23
|
+
# One parameter passed to block:
|
24
|
+
# error - string with error message
|
25
|
+
def onerror(&blk); @onerror = blk; end
|
26
|
+
|
27
|
+
# Called when message is received.
|
28
|
+
# Two parameters passed to block:
|
29
|
+
# message - string with received message
|
30
|
+
# type - type of message. Valid values are :text and :binary
|
31
|
+
def onmessage(&blk); @onmessage = blk; end
|
32
|
+
|
33
|
+
# Called when ping message is received
|
34
|
+
# One parameter passed to block:
|
35
|
+
# message - string with ping message
|
36
|
+
def onping(&blk); @onping = blk; end
|
37
|
+
|
38
|
+
# Called when pond message is received
|
39
|
+
# One parameter passed to block:
|
40
|
+
# message - string with pong message
|
41
|
+
def onpong(&blk); @onpong = blk; end
|
42
|
+
|
43
|
+
# Send data
|
44
|
+
# @param data [String] Data to send
|
45
|
+
# @param args [Hash] Arguments for send
|
46
|
+
# @option args [String] :type Type of frame to send - available types are "text", "binary", "ping", "pong" and "close"
|
47
|
+
# @option args [Integer] :code Code for close frame
|
48
|
+
# @return [Boolean] true if data was send, otherwise call on_error if needed
|
49
|
+
def send(data, args = {})
|
50
|
+
type = args[:type] || :text
|
51
|
+
unless type == :plain
|
52
|
+
frame = outgoing_frame.new(:version => @handshake.version, :data => data, :type => type.to_s, :code => args[:code])
|
53
|
+
if !frame.supported?
|
54
|
+
trigger_onerror("Frame type '#{type}' is not supported in protocol version #{@handshake.version}")
|
55
|
+
return false
|
56
|
+
elsif !frame.require_sending?
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
data = frame.to_s
|
60
|
+
end
|
61
|
+
debug "Sending raw: ", data
|
62
|
+
send_data(data)
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
# Close connection
|
67
|
+
# @return [Boolean] true if connection is closed immediately, false if waiting for other side to close connection
|
68
|
+
def close(code = 1000, data = nil)
|
69
|
+
if @state == :open
|
70
|
+
@state = :closing
|
71
|
+
return false if send(data, :type => :close, :code => code)
|
72
|
+
else
|
73
|
+
send(data, :type => :close) if @state == :closing
|
74
|
+
@state = :closed
|
75
|
+
end
|
76
|
+
close_connection_after_writing
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
# Send ping message
|
81
|
+
# @return [Boolean] false if protocol version is not supporting ping requests
|
82
|
+
def ping(data = '')
|
83
|
+
send(data, :type => :ping)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Send pong message
|
87
|
+
# @return [Boolean] false if protocol version is not supporting pong requests
|
88
|
+
def pong(data = '')
|
89
|
+
send(data, :type => :pong)
|
90
|
+
end
|
91
|
+
|
92
|
+
############################
|
93
|
+
### EventMachine methods ###
|
94
|
+
############################
|
95
|
+
|
96
|
+
# Eventmachine internal
|
97
|
+
# @private
|
98
|
+
def receive_data(data)
|
99
|
+
debug "Received raw: ", data
|
100
|
+
case @state
|
101
|
+
when :connecting then handle_connecting(data)
|
102
|
+
when :open then handle_open(data)
|
103
|
+
when :closing then handle_closing(data)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Eventmachine internal
|
108
|
+
# @private
|
109
|
+
def unbind
|
110
|
+
unless @state == :closed
|
111
|
+
@state = :closed
|
112
|
+
close
|
113
|
+
trigger_onclose('')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
#######################
|
118
|
+
### Private methods ###
|
119
|
+
#######################
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
['onopen'].each do |m|
|
124
|
+
define_method "trigger_#{m}" do
|
125
|
+
callback = instance_variable_get("@#{m}")
|
126
|
+
callback.call if callback
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
['onerror', 'onping', 'onpong', 'onclose'].each do |m|
|
131
|
+
define_method "trigger_#{m}" do |data|
|
132
|
+
callback = instance_variable_get("@#{m}")
|
133
|
+
callback.call(data) if callback
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def trigger_onmessage(data, type)
|
138
|
+
@onmessage.call(data, type) if @onmessage
|
139
|
+
end
|
140
|
+
|
141
|
+
def handle_connecting(data)
|
142
|
+
@handshake << data
|
143
|
+
return unless @handshake.finished?
|
144
|
+
if @handshake.valid?
|
145
|
+
send(@handshake.to_s, :type => :plain) if @handshake.should_respond?
|
146
|
+
@frame = incoming_frame.new(:version => @handshake.version)
|
147
|
+
@state = :open
|
148
|
+
trigger_onopen
|
149
|
+
handle_open(@handshake.leftovers) if @handshake.leftovers
|
150
|
+
else
|
151
|
+
trigger_onerror(@handshake.error)
|
152
|
+
close
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def handle_open(data)
|
157
|
+
@frame << data
|
158
|
+
while frame = @frame.next
|
159
|
+
case frame.type
|
160
|
+
when :close
|
161
|
+
@state = :closing
|
162
|
+
close
|
163
|
+
trigger_onclose(frame.to_s)
|
164
|
+
when :ping
|
165
|
+
pong(frame.to_s)
|
166
|
+
trigger_onping(frame.to_s)
|
167
|
+
when :pong
|
168
|
+
trigger_onpong(frame.to_s)
|
169
|
+
when :text
|
170
|
+
trigger_onmessage(frame.to_s, :text)
|
171
|
+
when :binary
|
172
|
+
trigger_onmessage(frame.to_s, :binary)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
unbind if @frame.error?
|
176
|
+
end
|
177
|
+
|
178
|
+
def handle_closing(data)
|
179
|
+
@state = :closed
|
180
|
+
close
|
181
|
+
trigger_onclose
|
182
|
+
end
|
183
|
+
|
184
|
+
def debug(description, data)
|
185
|
+
return unless @debug
|
186
|
+
puts(description + data.bytes.to_a.collect{|b| '\x' + b.to_s(16).rjust(2, '0')}.join) unless @state == :connecting
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path('../websocket/eventmachine/base', __FILE__)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "websocket/eventmachine/base/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "websocket-eventmachine-base"
|
7
|
+
s.version = WebSocket::EventMachine::Base::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Bernard Potocki"]
|
10
|
+
s.email = ["bernard.potocki@imanel.org"]
|
11
|
+
s.homepage = "http://github.com/imanel/websocket-eventmachine-base"
|
12
|
+
s.summary = %q{WebSocket base for Ruby client and server}
|
13
|
+
s.description = %q{WebSocket base for Ruby client and server}
|
14
|
+
|
15
|
+
s.add_dependency 'websocket', '~> 1.0'
|
16
|
+
s.add_dependency 'websocket-native', '~> 1.0'
|
17
|
+
s.add_dependency 'eventmachine', '~> 1.0'
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: websocket-eventmachine-base
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bernard Potocki
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: websocket
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: websocket-native
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: eventmachine
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
description: WebSocket base for Ruby client and server
|
63
|
+
email:
|
64
|
+
- bernard.potocki@imanel.org
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- CHANGELOG.md
|
71
|
+
- README.md
|
72
|
+
- Rakefile
|
73
|
+
- lib/websocket-eventmachine-base.rb
|
74
|
+
- lib/websocket/eventmachine/base.rb
|
75
|
+
- lib/websocket/eventmachine/base/version.rb
|
76
|
+
- websocket-eventmachine-base.gemspec
|
77
|
+
homepage: http://github.com/imanel/websocket-eventmachine-base
|
78
|
+
licenses: []
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.8.24
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: WebSocket base for Ruby client and server
|
101
|
+
test_files: []
|