wocket 0.0.1
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/.document +3 -0
- data/.gitignore +7 -0
- data/.rbenv-gemsets +1 -0
- data/.rspec +1 -0
- data/Gemfile +7 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +20 -0
- data/README.md +76 -0
- data/Rakefile +34 -0
- data/demo/client.rb +70 -0
- data/demo/server.rb +26 -0
- data/fuzzingclient.json +16 -0
- data/lib/wocket.rb +8 -0
- data/lib/wocket/bindable.rb +16 -0
- data/lib/wocket/server.rb +51 -0
- data/lib/wocket/version.rb +4 -0
- data/lib/wocket/websocket.rb +205 -0
- data/spec/lib/wocket/bindable_spec.rb +33 -0
- data/spec/lib/wocket/server_spec.rb +26 -0
- data/spec/lib/wocket/websocket_spec.rb +30 -0
- data/spec/lib/wocket_spec.rb +5 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/dummy_socket.rb +9 -0
- data/wocket.gemspec +33 -0
- metadata +257 -0
data/.document
ADDED
data/.gitignore
ADDED
data/.rbenv-gemsets
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ally
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format documentation
|
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Brady Love
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Wocket - 0.0.1
|
2
|
+
|
3
|
+
* [Homepage](https://github.com/bradylove/wocket#readme)
|
4
|
+
* [Issues](https://github.com/bradylove/wocket/issues)
|
5
|
+
|
6
|
+
## Description
|
7
|
+
|
8
|
+
Wocket is a simple WebSocket server and client built on top of Celluloid::IO.
|
9
|
+
Currently only the server portion of Wocket is useable. I have been testing Wocket
|
10
|
+
using [http://autobahn.ws/testsuite](http://autobahn.ws/testsuite) and only have a handful of cases where I am seeing red.
|
11
|
+
|
12
|
+
## Install
|
13
|
+
|
14
|
+
$ gem install wocket
|
15
|
+
|
16
|
+
## Examples
|
17
|
+
### Server
|
18
|
+
|
19
|
+
Save the following as a .rb file. Ex server.rb
|
20
|
+
|
21
|
+
require 'wocket'
|
22
|
+
server = Wocket::Server.new
|
23
|
+
|
24
|
+
server.bind(:onopen) do |ws|
|
25
|
+
puts "Client connected"
|
26
|
+
end
|
27
|
+
|
28
|
+
server.bind(:onmessage) do |ws, data, type|
|
29
|
+
puts "Message received"
|
30
|
+
end
|
31
|
+
|
32
|
+
server.bind(:onerror) do |ws, code, reason|
|
33
|
+
puts "ERROR: Code: #{code}, Reason: #{reason}"
|
34
|
+
end
|
35
|
+
|
36
|
+
server.bind(:onclose) do |ws, code, reason|
|
37
|
+
puts "Connection closed: #{code} - #{reason}"
|
38
|
+
end
|
39
|
+
|
40
|
+
server.start
|
41
|
+
|
42
|
+
trap("INT") { server.terminate; exit }
|
43
|
+
sleep
|
44
|
+
|
45
|
+
Then run it :)
|
46
|
+
|
47
|
+
$ ruby server.rb
|
48
|
+
|
49
|
+
This will start wocket on port 9292. More details coming soon :)
|
50
|
+
|
51
|
+
### Client
|
52
|
+
|
53
|
+
Not yet available.
|
54
|
+
|
55
|
+
## Copyright
|
56
|
+
|
57
|
+
Copyright (c) 2013 Brady Love
|
58
|
+
|
59
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
60
|
+
a copy of this software and associated documentation files (the
|
61
|
+
"Software"), to deal in the Software without restriction, including
|
62
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
63
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
64
|
+
permit persons to whom the Software is furnished to do so, subject to
|
65
|
+
the following conditions:
|
66
|
+
|
67
|
+
The above copyright notice and this permission notice shall be
|
68
|
+
included in all copies or substantial portions of the Software.
|
69
|
+
|
70
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
71
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
72
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
73
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
74
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
75
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
76
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bundler'
|
7
|
+
rescue LoadError => e
|
8
|
+
warn e.message
|
9
|
+
warn "Run `gem install bundler` to install Bundler."
|
10
|
+
exit -1
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Bundler.setup(:development)
|
15
|
+
rescue Bundler::BundlerError => e
|
16
|
+
warn e.message
|
17
|
+
warn "Run `bundle install` to install missing gems."
|
18
|
+
exit e.status_code
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake'
|
22
|
+
|
23
|
+
require 'rubygems/tasks'
|
24
|
+
Gem::Tasks.new
|
25
|
+
|
26
|
+
require 'rspec/core/rake_task'
|
27
|
+
RSpec::Core::RakeTask.new
|
28
|
+
|
29
|
+
task :test => :spec
|
30
|
+
task :default => :spec
|
31
|
+
|
32
|
+
require 'yard'
|
33
|
+
YARD::Rake::YardocTask.new
|
34
|
+
task :doc => :yard
|
data/demo/client.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'celluloid/io'
|
2
|
+
require 'celluloid/autostart'
|
3
|
+
require 'websocket'
|
4
|
+
require 'uri'
|
5
|
+
require 'securerandom'
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
class WSClient
|
9
|
+
include Celluloid
|
10
|
+
include Celluloid::IO
|
11
|
+
|
12
|
+
def initialize(uri, id)
|
13
|
+
puts "Connection ##{id}"
|
14
|
+
|
15
|
+
@version = 'unknown'
|
16
|
+
@id = id
|
17
|
+
@uri = URI(uri)
|
18
|
+
@socket = TCPSocket.new(@uri.host, @uri.port)
|
19
|
+
|
20
|
+
send_handshake
|
21
|
+
end
|
22
|
+
|
23
|
+
def send_handshake
|
24
|
+
@handshake = WebSocket::Handshake::Client.new(url: @uri.to_s)
|
25
|
+
@socket << @handshake.to_s
|
26
|
+
|
27
|
+
@handshake << @socket.readpartial(4096) until @handshake.finished?
|
28
|
+
|
29
|
+
if @handshake.valid?
|
30
|
+
@version = @handshake.version
|
31
|
+
puts "Valid Handshake ##{@id}"
|
32
|
+
|
33
|
+
async.send_random_data
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def read
|
38
|
+
frame = WebSocket::Frame::Incoming::Client.new(version: @version)
|
39
|
+
frame << @socket.readpartial(4096) until msg = frame.next
|
40
|
+
|
41
|
+
puts "Received: #{msg.data}"
|
42
|
+
|
43
|
+
send_random_data
|
44
|
+
end
|
45
|
+
|
46
|
+
# def start_timer
|
47
|
+
# cancel_timer!
|
48
|
+
# @timer = every(3) { send_random_data }
|
49
|
+
# end
|
50
|
+
|
51
|
+
# def cancel_timer!
|
52
|
+
# @timer && @timer.cancel
|
53
|
+
# end
|
54
|
+
|
55
|
+
def send_random_data
|
56
|
+
after(5) {
|
57
|
+
@socket << WebSocket::Frame::Outgoing::Client.new(version: @version, data: "Hello world! How are you doing today?", type: :text).to_s
|
58
|
+
puts "Sent frame"
|
59
|
+
|
60
|
+
read
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
ARGV[0].to_i.times do |i|
|
66
|
+
WSClient.supervise("ws://localhost:9292", i)
|
67
|
+
sleep(0.1)
|
68
|
+
end
|
69
|
+
|
70
|
+
sleep
|
data/demo/server.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'wocket'
|
2
|
+
|
3
|
+
server = Wocket::Server.new
|
4
|
+
|
5
|
+
server.bind(:onopen) do |ws|
|
6
|
+
puts "Client connected"
|
7
|
+
end
|
8
|
+
|
9
|
+
server.bind(:onmessage) do |ws, data, type|
|
10
|
+
puts "Message received"
|
11
|
+
|
12
|
+
ws.push data, type
|
13
|
+
end
|
14
|
+
|
15
|
+
server.bind(:onerror) do |ws, code, reason|
|
16
|
+
puts "ERROR: Code: #{code}, Reason: #{reason}"
|
17
|
+
end
|
18
|
+
|
19
|
+
server.bind(:onclose) do |ws, code, reason|
|
20
|
+
puts "Connection closed: #{code} - #{reason}"
|
21
|
+
end
|
22
|
+
|
23
|
+
server.start
|
24
|
+
|
25
|
+
trap("INT") { server.terminate; exit }
|
26
|
+
sleep
|
data/fuzzingclient.json
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
{
|
2
|
+
"options": {"failByDrop": false},
|
3
|
+
"outdir": "./reports/servers",
|
4
|
+
|
5
|
+
"servers": [
|
6
|
+
{
|
7
|
+
"agent": "Wocket 0.0.1",
|
8
|
+
"url": "ws://127.0.0.1:9292",
|
9
|
+
"options": {"version": 13}
|
10
|
+
}
|
11
|
+
],
|
12
|
+
|
13
|
+
"cases": ["*"],
|
14
|
+
"exclude-cases": [],
|
15
|
+
"exclude-agent-cases": {}
|
16
|
+
}
|
data/lib/wocket.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
module Wocket
|
2
|
+
ROOT = File.expand_path(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
autoload :Bindable, File.join(ROOT, 'wocket', 'bindable')
|
5
|
+
autoload :Server, File.join(ROOT, 'wocket', 'server')
|
6
|
+
autoload :VERSION, File.join(ROOT, 'wocket', 'version')
|
7
|
+
autoload :WebSocket, File.join(ROOT, 'wocket', 'websocket')
|
8
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Wocket
|
2
|
+
module Bindable
|
3
|
+
def available_bindings
|
4
|
+
raise NotImplementedError
|
5
|
+
end
|
6
|
+
|
7
|
+
def bind(name, &callback)
|
8
|
+
raise "Invalid binding: #{name}" unless available_bindings.include? name
|
9
|
+
@callbacks[name] = callback
|
10
|
+
end
|
11
|
+
|
12
|
+
def trigger(name, *args)
|
13
|
+
@callbacks[name].call *args if @callbacks[name]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'celluloid/io'
|
2
|
+
|
3
|
+
module Wocket
|
4
|
+
class Server
|
5
|
+
include Bindable
|
6
|
+
include Celluloid::IO
|
7
|
+
|
8
|
+
DEFAULT_HOST = "127.0.0.1"
|
9
|
+
DEFAULT_PORT = 9292
|
10
|
+
|
11
|
+
finalizer :finalize
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
@options = options
|
15
|
+
@host = options[:host] || DEFAULT_HOST
|
16
|
+
@port = options[:port] || DEFAULT_PORT
|
17
|
+
|
18
|
+
@callbacks = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def available_bindings
|
22
|
+
[:onopen, :onclose, :onerror, :onmessage, :onping, :onpong]
|
23
|
+
end
|
24
|
+
|
25
|
+
def start
|
26
|
+
@server = TCPServer.new(@host, @port)
|
27
|
+
async.run
|
28
|
+
puts startup_message
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
loop { async.handle_connection @server.accept }
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def startup_message
|
38
|
+
msg = "Wocket server now listening on port #{@port}\n"
|
39
|
+
msg += "-" * 45
|
40
|
+
msg
|
41
|
+
end
|
42
|
+
|
43
|
+
def finalize
|
44
|
+
@server.close if @server
|
45
|
+
end
|
46
|
+
|
47
|
+
def handle_connection(socket)
|
48
|
+
websocket = WebSocket.new(socket, self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'websocket'
|
2
|
+
require 'celluloid'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
module Wocket
|
6
|
+
class WebSocket
|
7
|
+
include Celluloid
|
8
|
+
|
9
|
+
class HandshakeError < RuntimeError; end
|
10
|
+
|
11
|
+
attr_reader :version, :state
|
12
|
+
|
13
|
+
BUFFER_SIZE = 4096
|
14
|
+
|
15
|
+
STATUS_CODES = {
|
16
|
+
normal_closure: 1000,
|
17
|
+
going_away: 1001,
|
18
|
+
protocol_error: 1002,
|
19
|
+
invalid_data: 1003,
|
20
|
+
inconsistent_data: 1007,
|
21
|
+
policy_violation: 1008,
|
22
|
+
too_large: 1009,
|
23
|
+
extension_error: 1010,
|
24
|
+
unexpected_condition: 1011
|
25
|
+
}
|
26
|
+
|
27
|
+
# Initializes a new Wocket WebSocket, verifies the client handshake, if
|
28
|
+
# the handhake is valid the server returns a handshake completing the
|
29
|
+
# handshake process.
|
30
|
+
# @param socket [TCPSocket] TCPSocket of the new connection
|
31
|
+
# @param server [Wocket::Server] The instance of the Wocket server that
|
32
|
+
# that the websocket belongs to
|
33
|
+
def initialize(socket, server)
|
34
|
+
@socket = socket
|
35
|
+
@server = server
|
36
|
+
@state = :connecting
|
37
|
+
@stop_read = false
|
38
|
+
|
39
|
+
validate_handshake
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns true if the client is connected, false if they are not.
|
43
|
+
def connected?
|
44
|
+
@state == :connected
|
45
|
+
end
|
46
|
+
|
47
|
+
# Writes data frame to the websocket.
|
48
|
+
# @param data [String] String of data to be pushed to the client.
|
49
|
+
# @param type [Symbol] The type of data to send to the client. Must be :text
|
50
|
+
# or :binary
|
51
|
+
def push(data = '', type = :text)
|
52
|
+
frame = outgoing_frame.new(version: @version, data: data, type: type)
|
53
|
+
@socket.write frame.to_s
|
54
|
+
end
|
55
|
+
alias_method :write, :push
|
56
|
+
|
57
|
+
|
58
|
+
# Closes the open websocket with a code and a reason defaulting to a normal
|
59
|
+
# closure, once connection is closed the :onclose callback is triggered.
|
60
|
+
# @param code [Integer] Disconnect status code.
|
61
|
+
# See http://tools.ietf.org/html/rfc6455#section-7.4.1
|
62
|
+
# @param reason [String] An explanation why the connection is being closed.
|
63
|
+
def close(code = :normal_closure, reason = "normal closure")
|
64
|
+
code = STATUS_CODES[code]
|
65
|
+
@stop_read = true
|
66
|
+
|
67
|
+
unless @socket.closed?
|
68
|
+
@socket.write outgoing_frame.new(version: @version,
|
69
|
+
data: reason,
|
70
|
+
type: :close,
|
71
|
+
code: code).to_s
|
72
|
+
|
73
|
+
@socket.close
|
74
|
+
end
|
75
|
+
|
76
|
+
@state = :disconnected
|
77
|
+
@server.trigger(:onclose, self, code, reason)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Send a ping message to the client
|
81
|
+
def ping
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def validate_handshake
|
88
|
+
handshake = ::WebSocket::Handshake::Server.new
|
89
|
+
handshake << readpartial until handshake.finished?
|
90
|
+
|
91
|
+
@version = handshake.version
|
92
|
+
|
93
|
+
if handshake.valid?
|
94
|
+
@socket.write handshake.to_s
|
95
|
+
@state = :connected
|
96
|
+
@server.trigger(:onopen, self)
|
97
|
+
|
98
|
+
@socket.wait_readable
|
99
|
+
async.read
|
100
|
+
else
|
101
|
+
@state = :invalid_handshake
|
102
|
+
@socket.write "HTTP/1.1 400 Bad Request"
|
103
|
+
@socket.close
|
104
|
+
|
105
|
+
raise HandshakeError, "Invalid handshake received, connection closed."
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def read
|
110
|
+
loop do
|
111
|
+
break if @stop_read
|
112
|
+
|
113
|
+
|
114
|
+
incoming_frame << readpartial until msg = incoming_frame.next || incoming_frame.error
|
115
|
+
|
116
|
+
if incoming_frame.error
|
117
|
+
handle_invalid_frame(incoming_frame)
|
118
|
+
|
119
|
+
@incoming_frame = nil
|
120
|
+
elsif msg
|
121
|
+
case msg.type
|
122
|
+
when :text
|
123
|
+
handle_text_message(msg)
|
124
|
+
when :binary
|
125
|
+
handle_binary_message(msg)
|
126
|
+
when :ping
|
127
|
+
handle_ping(msg)
|
128
|
+
when :close
|
129
|
+
handle_close(msg)
|
130
|
+
else
|
131
|
+
# Do nothing for now
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
rescue EOFError, Errno::ECONNRESET, IOError, Errno::EPIPE
|
136
|
+
puts "Socket Gone, where did it go?"
|
137
|
+
# close
|
138
|
+
end
|
139
|
+
|
140
|
+
def handle_text_message(msg)
|
141
|
+
case msg.data.size
|
142
|
+
when 0, *(125..128).to_a, *(65535..65536).to_a
|
143
|
+
push msg.data
|
144
|
+
else
|
145
|
+
@server.trigger(:onmessage, self, msg.data, msg.type)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def handle_binary_message(msg)
|
150
|
+
case msg.data.size
|
151
|
+
when 0, *(125..128).to_a, *(65535..65536).to_a
|
152
|
+
push msg.data, :binary
|
153
|
+
else
|
154
|
+
@server.trigger(:onmessage, self, msg.data, msg.type)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def handle_ping(msg)
|
159
|
+
if msg.data.length > 125
|
160
|
+
# For some reason websockets-ruby isn't passing on PINGS that are too large
|
161
|
+
close :protocol_error, "ping message too large"
|
162
|
+
else
|
163
|
+
frame = outgoing_frame.new(version: @version, type: :pong, data: msg.data)
|
164
|
+
@socket << frame.to_s
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def handle_close(msg)
|
169
|
+
close
|
170
|
+
end
|
171
|
+
|
172
|
+
def handle_invalid_frame(invalid_frame)
|
173
|
+
case incoming_frame.error
|
174
|
+
when :control_frame_payload_too_long
|
175
|
+
close :protocol_error, "protocol error: control frame too long"
|
176
|
+
when :reserved_bit_used
|
177
|
+
close :protocol_error, "protocol error: reserved bit used"
|
178
|
+
when :unknown_opcode
|
179
|
+
close :protocol_error, "protocol error: unknown opcode"
|
180
|
+
when :fragmented_control_frame
|
181
|
+
close :protocol_error, "protocol error: fragmented control frame"
|
182
|
+
when :unexpected_continuation_frame
|
183
|
+
close :protocol_error, "protocol error: unexpected continuation frame"
|
184
|
+
when :data_frame_instead_continuation
|
185
|
+
close :protocol_error, "protocol error: data frame instead continuation"
|
186
|
+
when :invalid_payload_encoding
|
187
|
+
close :inconsistent_data, "inconsistent data error: invalid payload encoding"
|
188
|
+
else
|
189
|
+
puts incoming_frame.error
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def readpartial
|
194
|
+
@socket.readpartial(BUFFER_SIZE)
|
195
|
+
end
|
196
|
+
|
197
|
+
def incoming_frame
|
198
|
+
@incoming_frame ||= ::WebSocket::Frame::Incoming::Server.new(version: @version)
|
199
|
+
end
|
200
|
+
|
201
|
+
def outgoing_frame
|
202
|
+
::WebSocket::Frame::Outgoing::Server
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class BindableDummy
|
4
|
+
include Wocket::Bindable
|
5
|
+
|
6
|
+
attr_accessor :callbacks
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@callbacks = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def available_bindings
|
13
|
+
[:onconnect, :onerror]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe Wocket::Bindable do
|
18
|
+
let(:dummy) { BindableDummy.new }
|
19
|
+
|
20
|
+
before do
|
21
|
+
dummy.bind(:onconnect) do |x, y|
|
22
|
+
1001
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should bind a block to @callbacks" do
|
27
|
+
expect(dummy.callbacks[:onconnect].call).to eq 1001
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should execute the block when triggered" do
|
31
|
+
expect(dummy.trigger(:onconnect, 1, 2)).to eq 1001
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rspec/autorun'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Wocket::Server do
|
5
|
+
let(:server) { Wocket::Server.new }
|
6
|
+
|
7
|
+
# These specs dont work with celluloid
|
8
|
+
#
|
9
|
+
# context "initialization" do
|
10
|
+
# before do
|
11
|
+
# TCPServer.stub(:new).and_return(Object.new)
|
12
|
+
# end
|
13
|
+
|
14
|
+
# it "should have a default host" do
|
15
|
+
# expect(server.instance_variable_get(:@host)).to eq "127.0.0.1"
|
16
|
+
# end
|
17
|
+
|
18
|
+
# it "should have a default port" do
|
19
|
+
# expect(server.instance_variable_get(:@port)).to eq 9292
|
20
|
+
# end
|
21
|
+
|
22
|
+
# it "should create a new TCPServer" do
|
23
|
+
# expect(server.instance_variable_get(:@server)).to_not be_nil
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wocket::WebSocket do
|
4
|
+
let(:dummy_socket) { DummySocket.new }
|
5
|
+
let(:websocket) { Wocket::WebSocket.new dummy_socket, {} }
|
6
|
+
let(:client_header) { dummy_socket.readpartial(4096) }
|
7
|
+
|
8
|
+
# context "initialization" do
|
9
|
+
# it "should have the socket" do
|
10
|
+
# expect(websocket.instance_variable_get(:@socket)).to eq dummy_socket
|
11
|
+
# end
|
12
|
+
|
13
|
+
# it "should have a hash of options" do
|
14
|
+
# expect(websocket.instance_variable_get(:@options)).to eq({})
|
15
|
+
# end
|
16
|
+
|
17
|
+
# it "should create a new handshake object and read the clients header" do
|
18
|
+
# expect(websocket.instance_variable_get(:@handshake).valid?).to be_true
|
19
|
+
# end
|
20
|
+
|
21
|
+
# it "should get the version from the header" do
|
22
|
+
# expect(websocket.version).to eq 13
|
23
|
+
# end
|
24
|
+
|
25
|
+
# it "should send the accept header back to the client" do
|
26
|
+
# dummy_socket.should_receive(:write)
|
27
|
+
# Wocket::WebSocket.new dummy_socket, {}
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/wocket.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../lib/wocket/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "wocket"
|
7
|
+
gem.version = Wocket::VERSION
|
8
|
+
gem.summary = %q{Simple WebSocket server built on top of Celluloid::IO}
|
9
|
+
gem.description = %q{Simple WebSocket server built on top of Celluloid::IO}
|
10
|
+
gem.license = "MIT"
|
11
|
+
gem.authors = ["Brady Love"]
|
12
|
+
gem.email = "love.brady@gmail.com"
|
13
|
+
gem.homepage = "https://github.com/bradylove/wocket#readme"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
|
21
|
+
gem.add_runtime_dependency 'celluloid-io'
|
22
|
+
gem.add_runtime_dependency 'websocket'
|
23
|
+
gem.add_runtime_dependency 'websocket-native'
|
24
|
+
|
25
|
+
gem.add_development_dependency 'bundler', '~> 1.0'
|
26
|
+
gem.add_development_dependency 'rake', '~> 0.8'
|
27
|
+
gem.add_development_dependency 'rspec', '~> 2.4'
|
28
|
+
gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
29
|
+
gem.add_development_dependency 'yard', '~> 0.8'
|
30
|
+
gem.add_development_dependency 'guard-rspec'
|
31
|
+
gem.add_development_dependency 'rb-inotify'
|
32
|
+
gem.add_development_dependency 'pry-nav'
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wocket
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brady Love
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: celluloid-io
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '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: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: websocket
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '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: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: websocket-native
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '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: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rake
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0.8'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0.8'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rspec
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '2.4'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.4'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rubygems-tasks
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.2'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0.2'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: yard
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0.8'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0.8'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: guard-rspec
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: rb-inotify
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: pry-nav
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
type: :development
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
description: Simple WebSocket server built on top of Celluloid::IO
|
191
|
+
email: love.brady@gmail.com
|
192
|
+
executables: []
|
193
|
+
extensions: []
|
194
|
+
extra_rdoc_files: []
|
195
|
+
files:
|
196
|
+
- .document
|
197
|
+
- .gitignore
|
198
|
+
- .rbenv-gemsets
|
199
|
+
- .rspec
|
200
|
+
- Gemfile
|
201
|
+
- Guardfile
|
202
|
+
- LICENSE.txt
|
203
|
+
- README.md
|
204
|
+
- Rakefile
|
205
|
+
- demo/client.rb
|
206
|
+
- demo/server.rb
|
207
|
+
- fuzzingclient.json
|
208
|
+
- lib/wocket.rb
|
209
|
+
- lib/wocket/bindable.rb
|
210
|
+
- lib/wocket/server.rb
|
211
|
+
- lib/wocket/version.rb
|
212
|
+
- lib/wocket/websocket.rb
|
213
|
+
- spec/lib/wocket/bindable_spec.rb
|
214
|
+
- spec/lib/wocket/server_spec.rb
|
215
|
+
- spec/lib/wocket/websocket_spec.rb
|
216
|
+
- spec/lib/wocket_spec.rb
|
217
|
+
- spec/spec_helper.rb
|
218
|
+
- spec/support/dummy_socket.rb
|
219
|
+
- wocket.gemspec
|
220
|
+
homepage: https://github.com/bradylove/wocket#readme
|
221
|
+
licenses:
|
222
|
+
- MIT
|
223
|
+
post_install_message:
|
224
|
+
rdoc_options: []
|
225
|
+
require_paths:
|
226
|
+
- lib
|
227
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
228
|
+
none: false
|
229
|
+
requirements:
|
230
|
+
- - ! '>='
|
231
|
+
- !ruby/object:Gem::Version
|
232
|
+
version: '0'
|
233
|
+
segments:
|
234
|
+
- 0
|
235
|
+
hash: 47223995340184469
|
236
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
237
|
+
none: false
|
238
|
+
requirements:
|
239
|
+
- - ! '>='
|
240
|
+
- !ruby/object:Gem::Version
|
241
|
+
version: '0'
|
242
|
+
segments:
|
243
|
+
- 0
|
244
|
+
hash: 47223995340184469
|
245
|
+
requirements: []
|
246
|
+
rubyforge_project:
|
247
|
+
rubygems_version: 1.8.25
|
248
|
+
signing_key:
|
249
|
+
specification_version: 3
|
250
|
+
summary: Simple WebSocket server built on top of Celluloid::IO
|
251
|
+
test_files:
|
252
|
+
- spec/lib/wocket/bindable_spec.rb
|
253
|
+
- spec/lib/wocket/server_spec.rb
|
254
|
+
- spec/lib/wocket/websocket_spec.rb
|
255
|
+
- spec/lib/wocket_spec.rb
|
256
|
+
- spec/spec_helper.rb
|
257
|
+
- spec/support/dummy_socket.rb
|