forked from shokai/websocket-client-simple
/
client.rb
112 lines (101 loc) · 3.2 KB
/
client.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
module WebSocket
module Client
module Simple
def self.connect(url, options={})
client = ::WebSocket::Client::Simple::Client.new
yield client if block_given?
client.connect url, options
return client
end
class Client
include EventEmitter
attr_reader :url, :handshake, :thread
def connect(url, options={})
return if @socket
@url = url
uri = URI.parse url
@socket = TCPSocket.new(uri.host,
uri.port || (uri.scheme == 'wss' ? 443 : 80))
if ['https', 'wss'].include? uri.scheme
ctx = OpenSSL::SSL::SSLContext.new
ctx.ssl_version = options[:ssl_version] if options[:ssl_version]
ctx.verify_mode = options[:verify_mode] if options[:verify_mode]
cert_store = options[:cert_store] || OpenSSL::X509::Store.new
cert_store.set_default_paths
ctx.cert_store = cert_store
@socket = ::OpenSSL::SSL::SSLSocket.new(@socket, ctx)
@socket.sync_close = true
@socket.hostname = uri.host
@socket.connect
end
::WebSocket.should_raise = true
@handshake = ::WebSocket::Handshake::Client.new :url => url, :headers => options[:headers]
@handshaked = false
@pipe_broken = false
frame = ::WebSocket::Frame::Incoming::Client.new
@closed = false
once :__close do |err|
close
emit :close, err
end
@thread = Thread.new do
while !@closed do
begin
unless recv_data = @socket.getc
sleep 1
next
end
unless @handshaked
@handshake << recv_data
if @handshake.finished?
@handshaked = true
emit :open
end
else
frame << recv_data
while msg = frame.next
emit :message, msg
end
end
rescue => e
emit :error, e
end
end
end
@socket.write @handshake.to_s
end
def send(data, opt={:type => :text})
return if !@handshaked or @closed
type = opt[:type]
frame = ::WebSocket::Frame::Outgoing::Client.new(:data => data, :type => type, :version => @handshake.version)
begin
@socket.write frame.to_s
rescue Errno::EPIPE => e
@pipe_broken = true
emit :__close, e
rescue OpenSSL::SSL::SSLError => e
@pipe_broken = true
emit :__close, e
end
end
def close
return if @closed
if !@pipe_broken
send nil, :type => :close
end
@closed = true
@socket.close if @socket
@socket = nil
emit :__close
Thread.kill @thread if @thread
end
def open?
@handshake&.finished? and !@closed
end
def closed?
@closed
end
end
end
end
end