Skip to content

Commit bdc8315

Browse files
committed
Example chat client/server.
1 parent 78d6fdc commit bdc8315

File tree

6 files changed

+215
-60
lines changed

6 files changed

+215
-60
lines changed

chat/client.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'async/reactor'
4+
require 'async/io/stream'
5+
require 'async/http/url_endpoint'
6+
require 'async/websocket/client'
7+
8+
URL = ARGV.pop
9+
USER = ARGV.pop
10+
11+
Async::Reactor.run do |task|
12+
endpoint = Async::HTTP::URLEndpoint.parse(URL)
13+
14+
endpoint.connect do |socket|
15+
connection = Async::WebSocket::Client.new(socket, URL)
16+
17+
connection.send_message({
18+
user: USER,
19+
status: "connected",
20+
})
21+
22+
task.async do
23+
stdin = Async::IO::Stream.new(
24+
Async::IO::Generic.new($stdin)
25+
)
26+
27+
puts "Waiting for input..."
28+
while line = stdin.read_until("\n")
29+
puts "Sending text: #{line}"
30+
connection.send_message({
31+
user: USER,
32+
text: line,
33+
})
34+
end
35+
end
36+
37+
while message = connection.next_message
38+
puts "Message from server: #{message.inspect}"
39+
end
40+
end
41+
end

chat/config.ru

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env falcon serve --concurrency 1 -c
2+
3+
require 'async/websocket/server'
4+
5+
Message = Struct.new(:user, :contents, :created_at)
6+
7+
$connections = []
8+
9+
run lambda {|env|
10+
Async::WebSocket::Server.open(env) do |connection|
11+
$connections << connection
12+
13+
while message = connection.next_message
14+
$connections.each do |connection|
15+
connection.send_message(message)
16+
end
17+
end
18+
end
19+
20+
[200, {}, ["Hello World"]]
21+
}

lib/async/websocket/client.rb

Lines changed: 4 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,71 +18,16 @@
1818
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1919
# THE SOFTWARE.
2020

21-
require 'websocket/driver'
22-
require 'json'
21+
require_relative 'connection'
2322

2423
module Async
2524
module WebSocket
2625
# This is a basic synchronous websocket client:
27-
class Client
28-
EVENTS = [:open, :message, :close]
29-
30-
def initialize(socket, url: "ws://.")
31-
@socket = socket
26+
class Client < Connection
27+
def initialize(socket, url = "ws://.")
3228
@url = url
3329

34-
@driver = ::WebSocket::Driver.client(self)
35-
36-
@queue = []
37-
38-
@driver.on(:error) do |error|
39-
raise error
40-
end
41-
42-
EVENTS.each do |event|
43-
@driver.on(event) do |data|
44-
@queue.push(data)
45-
end
46-
end
47-
48-
@driver.start
49-
end
50-
51-
attr :driver
52-
attr :url
53-
54-
def next_event
55-
while @queue.empty?
56-
data = @socket.read(1024)
57-
58-
if data and !data.empty?
59-
@driver.parse(data)
60-
else
61-
return nil
62-
end
63-
end
64-
65-
@queue.shift
66-
rescue EOFError
67-
return nil
68-
end
69-
70-
def next_message
71-
while event = next_event
72-
if event.is_a? ::WebSocket::Driver::MessageEvent
73-
return JSON.parse(event.data)
74-
elsif event.is_a? ::WebSocket::Driver::CloseEvent
75-
return nil
76-
end
77-
end
78-
end
79-
80-
def write(data)
81-
@socket.write(data)
82-
end
83-
84-
def close
85-
@driver.close
30+
super socket, ::WebSocket::Driver.client(self)
8631
end
8732
end
8833
end

lib/async/websocket/connection.rb

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Copyright, 2015, by Samuel G. D. Williams. <http://www.codeotaku.com>
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
# THE SOFTWARE.
20+
21+
require 'websocket/driver'
22+
require 'json'
23+
24+
module Async
25+
module WebSocket
26+
# This is a basic synchronous websocket client:
27+
class Connection
28+
EVENTS = [:open, :message, :close]
29+
30+
def initialize(socket, driver)
31+
@socket = socket
32+
@driver = driver
33+
34+
@queue = []
35+
36+
@driver.on(:error) do |error|
37+
raise error
38+
end
39+
40+
EVENTS.each do |event|
41+
@driver.on(event) do |data|
42+
@queue.push(data)
43+
end
44+
end
45+
46+
@driver.start
47+
end
48+
49+
attr :driver
50+
attr :url
51+
52+
def next_event
53+
while @queue.empty?
54+
data = @socket.read(1024)
55+
56+
if data and !data.empty?
57+
@driver.parse(data)
58+
else
59+
return nil
60+
end
61+
end
62+
63+
@queue.shift
64+
rescue EOFError
65+
return nil
66+
end
67+
68+
def next_message
69+
while event = next_event
70+
if event.is_a? ::WebSocket::Driver::MessageEvent
71+
return JSON.parse(event.data)
72+
elsif event.is_a? ::WebSocket::Driver::CloseEvent
73+
return nil
74+
end
75+
end
76+
end
77+
78+
def send_message(message)
79+
@driver.text(JSON.dump(message))
80+
end
81+
82+
def write(data)
83+
@socket.write(data)
84+
end
85+
86+
def close
87+
@driver.close
88+
end
89+
end
90+
end
91+
end

lib/async/websocket/server.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright, 2015, by Samuel G. D. Williams. <http://www.codeotaku.com>
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
# THE SOFTWARE.
20+
21+
require_relative 'connection'
22+
23+
module Async
24+
module WebSocket
25+
class Server < Connection
26+
def initialize(env, socket)
27+
scheme = env['rack.url_scheme'] == 'https' ? 'wss' : 'ws'
28+
@url = "#{scheme}://#{env['HTTP_HOST']}#{env['REQUEST_URI']}"
29+
30+
@env = env
31+
32+
super socket, ::WebSocket::Driver.rack(self)
33+
end
34+
35+
attr :env
36+
attr :url
37+
38+
def self.open(env)
39+
if ::WebSocket::Driver.websocket?(env)
40+
env['rack.hijack'].call
41+
42+
connection = self.new(env, env['rack.hijack_io'])
43+
44+
if block_given?
45+
begin
46+
yield connection
47+
ensure
48+
connection.close
49+
end
50+
else
51+
return connection
52+
end
53+
end
54+
end
55+
end
56+
end
57+
end

lib/async/websocket/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@
2020

2121
module Async
2222
module WebSocket
23-
VERSION = "0.2.0"
23+
VERSION = "0.3.0"
2424
end
2525
end

0 commit comments

Comments
 (0)