websocket-eventmachine-base 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|