simpleblockingwebsocketclient 0.42 → 0.43

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bae507dffc762b0aba09d3d64e9ff29d15cd1447
4
+ data.tar.gz: 2589a59049b591e570e54637f2c0b127fe44c044
5
+ SHA512:
6
+ metadata.gz: 32bc7ee3726a3c89525d5a423a337ddb29b1f407b05c9b441e4b4a6eb12a436124822912a20b4d232107af8791a8174721682f5870a06ff4cae1538b8ca407ac
7
+ data.tar.gz: 8b28ec38ebd303d2458c72ebc9536a9e89e365115f8961826cd82a44f7f1d878ed54ccc004e2db95cb94197b404f9b9664849fc7247619983e3da080fa1076e5
data/.gitignore CHANGED
@@ -1 +1 @@
1
- exp
1
+ .idea
data/Gemfile.lock ADDED
@@ -0,0 +1,14 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ simple (0.43)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+
10
+ PLATFORMS
11
+ ruby
12
+
13
+ DEPENDENCIES
14
+ simple!
data/lib/ws.rb ADDED
@@ -0,0 +1,210 @@
1
+ #
2
+ # = net/ws.rb
3
+ #
4
+ # Copyright (c) 2013-2013 Marvin Frick
5
+ #
6
+ # Written and maintained by Marvin Frick <marvinfrick@gmx.de>.
7
+ #
8
+ #
9
+ # This program is free software. You can re-distribute and/or
10
+ # modify this program under the same terms of ruby itself ---
11
+ # Ruby Distribution License or GNU General Public License.
12
+ #
13
+ # See Net::WS for an overview and examples.
14
+ #
15
+
16
+ require 'net/http'
17
+ require "digest/sha1"
18
+ require "base64"
19
+
20
+ class Net::WSError < Net::ProtocolError
21
+ def initialize(msg, res=nil) #:nodoc:
22
+ super msg
23
+ @response = res
24
+ end
25
+
26
+ attr_reader :response
27
+ end
28
+
29
+ module Net #:nodoc:
30
+ autoload :OpenSSL, 'openssl'
31
+
32
+ class WS < Protocol
33
+ class << self
34
+ attr_accessor(:debug)
35
+ end
36
+
37
+ WSVersion = "13"
38
+ WS_MAGIC_STRING = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
39
+ NOISE_CHARS = ("\x21".."\x2f").to_a() + ("\x3a".."\x7e").to_a()
40
+ OPCODE_CONTINUATION = 0x00
41
+ OPCODE_TEXT = 0x01
42
+ OPCODE_BINARY = 0x02
43
+ OPCODE_CLOSE = 0x08
44
+ OPCODE_PING = 0x09
45
+ OPCODE_PONG = 0x0a
46
+
47
+ def initialize(arg)
48
+ uri = arg.is_a?(String) ? URI.parse(arg) : arg
49
+ origin = "http://#{uri.host}"
50
+ key = generate_key
51
+
52
+ http = HTTP.new(uri.host, uri.port).start
53
+ handshake = http.send_request("GET", uri.path.empty? ? "/" : uri.path, nil, initheader(key, origin))
54
+ if handshake["sec-websocket-accept"] != security_digest(key)
55
+ raise Net::WSError.new("Sec-Websocket-Accept missmatch", handshake)
56
+ end
57
+ @handshaked = true
58
+ @socket = http.instance_variable_get(:@socket).io
59
+
60
+ if block_given?
61
+ @rth = Thread.new do
62
+ while data = receive()
63
+ yield data
64
+ end
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ def gets(rs = $/)
71
+ line = @socket.gets(rs)
72
+ $stderr.printf("recv> %p\n", line) if Net::WS.debug
73
+ return line
74
+ end
75
+
76
+ def read(num_bytes)
77
+ str = @socket.read(num_bytes)
78
+ $stderr.printf("recv> %p\n", str) if Net::WS.debug
79
+ if str && str.bytesize == num_bytes
80
+ return str
81
+ else
82
+ raise(EOFError)
83
+ end
84
+ end
85
+
86
+ def receive
87
+ if !@handshaked
88
+ raise Net::WSError.new("call WebSocket\#handshake first")
89
+ end
90
+
91
+ begin
92
+ bytes = read(2).unpack("C*")
93
+ fin = (bytes[0] & 0x80) != 0
94
+ opcode = bytes[0] & 0x0f
95
+ mask = (bytes[1] & 0x80) != 0
96
+ plength = bytes[1] & 0x7f
97
+ if plength == 126
98
+ bytes = read(2)
99
+ plength = bytes.unpack("n")[0]
100
+ elsif plength == 127
101
+ bytes = read(8)
102
+ (high, low) = bytes.unpack("NN")
103
+ plength = high * (2 ** 32) + low
104
+ end
105
+ if @server && !mask
106
+ # Masking is required.
107
+ @socket.close()
108
+ raise(WSError, "received unmasked data")
109
+ end
110
+ mask_key = mask ? read(4).unpack("C*") : nil
111
+ payload = read(plength)
112
+ payload = apply_mask(payload, mask_key) if mask
113
+ case opcode
114
+ when OPCODE_TEXT
115
+ return payload
116
+ when OPCODE_BINARY
117
+ raise(WebSocket::Error, "received binary data, which is not supported")
118
+ when OPCODE_CLOSE
119
+ close(1005, "", :peer)
120
+ return nil
121
+ when OPCODE_PING
122
+ raise(WebSocket::Error, "received ping, which is not supported")
123
+ when OPCODE_PONG
124
+ else
125
+ raise(WebSocket::Error, "received unknown opcode: %d" % opcode)
126
+ end
127
+ rescue EOFError
128
+ return nil
129
+ end
130
+ end
131
+
132
+ def write(data)
133
+ if WS.debug
134
+ puts data
135
+ data.scan(/\G(.*?(\n|\z))/n) do
136
+ $stderr.printf("send> %p\n", $&) if !$&.empty?
137
+ end
138
+ end
139
+ @socket.write(data)
140
+ end
141
+
142
+ def send(data)
143
+ if !@handshaked
144
+ raise Net::WSError.new("call WebSocket\#handshake first")
145
+ else
146
+ send_frame(OPCODE_TEXT, data, false)
147
+ end
148
+ end
149
+
150
+ def send_frame(opcode, payload, mask)
151
+ buffer = StringIO.new()
152
+ buffer.set_encoding("UTF-8")
153
+ write_byte(buffer, 0x80 | opcode)
154
+ masked_byte = mask ? 0x80 : 0x00
155
+ if payload.bytesize <= 125
156
+ write_byte(buffer, masked_byte | payload.bytesize)
157
+ elsif payload.bytesize < 2 ** 16
158
+ write_byte(buffer, masked_byte | 126)
159
+ buffer.write([payload.bytesize].pack("n"))
160
+ else
161
+ write_byte(buffer, masked_byte | 127)
162
+ buffer.write([payload.bytesize / (2 ** 32), payload.bytesize % (2 ** 32)].pack("NN"))
163
+ end
164
+ if mask
165
+ mask_key = Array.new(4) { rand(256) }
166
+ buffer.write(mask_key.pack("C*"))
167
+ payload = apply_mask(payload, mask_key)
168
+ end
169
+ buffer.write(payload)
170
+ write(buffer.string)
171
+ end
172
+
173
+ def write_byte(buffer, byte)
174
+ buffer.write([byte].pack("C"))
175
+ end
176
+
177
+ def initheader(key, origin)
178
+ {
179
+ "Upgrade" => "websocket",
180
+ "Connection" => "Upgrade",
181
+ "Sec-WebSocket-Key" => "#{key}",
182
+ "Sec-WebSocket-Version" => WSVersion,
183
+ "Origin" => "#{origin}"
184
+ }
185
+ end
186
+
187
+ def generate_key
188
+ spaces = 1 + rand(12)
189
+ max = 0xffffffff / spaces
190
+ number = rand(max + 1)
191
+ key = (number * spaces).to_s()
192
+ (1 + rand(12)).times() do
193
+ char = NOISE_CHARS[rand(NOISE_CHARS.size)]
194
+ pos = rand(key.size + 1)
195
+ key[pos...pos] = char
196
+ end
197
+ spaces.times() do
198
+ pos = 1 + rand(key.size - 1)
199
+ key[pos...pos] = " "
200
+ end
201
+ Base64.encode64(key).chop
202
+ end
203
+
204
+ def security_digest(key)
205
+ Base64.encode64(Digest::SHA1.digest(key + WS_MAGIC_STRING)).gsub(/\n/, "")
206
+ end
207
+
208
+ end
209
+
210
+ end
@@ -0,0 +1,25 @@
1
+ # this file makes use of EventMachine and its purpose is
2
+ # solely to have a server to connect to for testing the client lib.
3
+
4
+ require 'em-websocket'
5
+
6
+ EM.run {
7
+ EM::WebSocket.run(:host => "0.0.0.0", :port => 8080) do |ws|
8
+ ws.onopen { |handshake|
9
+ puts "WebSocket connection open"
10
+
11
+ # Access properties on the EM::WebSocket::Handshake object, e.g.
12
+ # path, query_string, origin, headers
13
+
14
+ # Publish message to the client
15
+ ws.send "Hello Client, you connected to #{handshake.path}"
16
+ }
17
+
18
+ ws.onclose { puts "Connection closed" }
19
+
20
+ ws.onmessage { |msg|
21
+ puts "Recieved message: #{msg}"
22
+ ws.send "Pong: #{msg}"
23
+ }
24
+ end
25
+ }
@@ -1,15 +1,13 @@
1
- # Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
2
- # Lincense: New BSD Lincense
3
1
 
4
2
  $LOAD_PATH << File.dirname(__FILE__) + "/../lib"
5
- require "web_socket"
3
+ require "ws"
6
4
 
7
5
  if ARGV.size != 1
8
6
  $stderr.puts("Usage: ruby samples/stdio_client.rb ws://HOST:PORT/")
9
7
  exit(1)
10
8
  end
11
9
 
12
- client = WebSocket.new(ARGV[0]) { |data| puts data}
10
+ client = Net::WS.new(ARGV[0]) { |data| puts data }
13
11
  puts("Connected")
14
12
 
15
13
  $stdin.each_line() do |line|
@@ -4,7 +4,7 @@ Gem::Specification.new do |gem|
4
4
  gem.authors = ["Marvin Frick"]
5
5
  gem.email = ["frick@informatik.uni-luebeck.de"]
6
6
  gem.description = %q{Ruby gem to connect to a websocket server}
7
- gem.summary = %q{Allows you to connect to a websocket server-side, using the hixie-76 protocol version. Sending and receiving data is supported. That's it.}
7
+ gem.summary = %q{Allows you to connect to a websocket server-side, using the RFC 6455 protocol version. Sending and receiving data is supported. That's it.}
8
8
  gem.homepage = "https://github.com/MrMarvin/simpleblockingwebsocketclient"
9
9
 
10
10
  gem.files = `git ls-files`.split($\)
@@ -12,5 +12,5 @@ Gem::Specification.new do |gem|
12
12
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
13
  gem.name = "simpleblockingwebsocketclient"
14
14
  gem.require_paths = ["lib"]
15
- gem.version = "0.42"
15
+ gem.version = "0.43"
16
16
  end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simpleblockingwebsocketclient
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.42'
5
- prerelease:
4
+ version: '0.43'
6
5
  platform: ruby
7
6
  authors:
8
7
  - Marvin Frick
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-12-18 00:00:00.000000000 Z
11
+ date: 2013-03-06 00:00:00.000000000 Z
13
12
  dependencies: []
14
13
  description: Ruby gem to connect to a websocket server
15
14
  email:
@@ -20,35 +19,36 @@ extra_rdoc_files: []
20
19
  files:
21
20
  - .gitignore
22
21
  - Gemfile
22
+ - Gemfile.lock
23
23
  - README.md
24
24
  - Rakefile
25
- - lib/simpleblockingwebsocketclient.rb
25
+ - lib/ws.rb
26
26
  - pkg/simpleblockingwebsocketclient-0.42.gem
27
+ - samples/simple_echo_server.rb
27
28
  - samples/stdio_client.rb
28
29
  - simpleblockingwebsocketclient.gemspec
29
30
  homepage: https://github.com/MrMarvin/simpleblockingwebsocketclient
30
31
  licenses: []
32
+ metadata: {}
31
33
  post_install_message:
32
34
  rdoc_options: []
33
35
  require_paths:
34
36
  - lib
35
37
  required_ruby_version: !ruby/object:Gem::Requirement
36
38
  requirements:
37
- - - ! '>='
39
+ - - '>='
38
40
  - !ruby/object:Gem::Version
39
41
  version: '0'
40
- none: false
41
42
  required_rubygems_version: !ruby/object:Gem::Requirement
42
43
  requirements:
43
- - - ! '>='
44
+ - - '>='
44
45
  - !ruby/object:Gem::Version
45
46
  version: '0'
46
- none: false
47
47
  requirements: []
48
48
  rubyforge_project:
49
- rubygems_version: 1.8.24
49
+ rubygems_version: 2.0.0
50
50
  signing_key:
51
- specification_version: 3
52
- summary: Allows you to connect to a websocket server-side, using the hixie-76 protocol
51
+ specification_version: 4
52
+ summary: Allows you to connect to a websocket server-side, using the RFC 6455 protocol
53
53
  version. Sending and receiving data is supported. That's it.
54
54
  test_files: []
@@ -1,405 +0,0 @@
1
- require "base64"
2
- require "socket"
3
- require "uri"
4
- require "digest/md5"
5
- require "digest/sha1"
6
- require "openssl"
7
- require "stringio"
8
-
9
-
10
- class WebSocket
11
-
12
- class << self
13
-
14
- attr_accessor(:debug)
15
-
16
- end
17
-
18
- class Error < RuntimeError
19
-
20
- end
21
-
22
- WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
23
- OPCODE_CONTINUATION = 0x00
24
- OPCODE_TEXT = 0x01
25
- OPCODE_BINARY = 0x02
26
- OPCODE_CLOSE = 0x08
27
- OPCODE_PING = 0x09
28
- OPCODE_PONG = 0x0a
29
-
30
- def initialize(arg, params = {})
31
-
32
- @web_socket_version = "hixie-76"
33
- uri = arg.is_a?(String) ? URI.parse(arg) : arg
34
-
35
- if uri.scheme == "ws"
36
- default_port = 80
37
- elsif uri.scheme = "wss"
38
- default_port = 443
39
- else
40
- raise(WebSocket::Error, "unsupported scheme: #{uri.scheme}")
41
- end
42
-
43
- @path = (uri.path.empty? ? "/" : uri.path) + (uri.query ? "?" + uri.query : "")
44
- host = uri.host + ((!uri.port || uri.port == default_port) ? "" : ":#{uri.port}")
45
- origin = params[:origin] || "http://#{uri.host}"
46
- key1 = generate_key()
47
- key2 = generate_key()
48
- key3 = generate_key3()
49
-
50
- socket = TCPSocket.new(uri.host, uri.port || default_port)
51
-
52
- if uri.scheme == "ws"
53
- @socket = socket
54
- else
55
- @socket = ssl_handshake(socket)
56
- end
57
-
58
- write(
59
- "GET #{@path} HTTP/1.1\r\n" +
60
- "Upgrade: WebSocket\r\n" +
61
- "Connection: Upgrade\r\n" +
62
- "Host: #{host}\r\n" +
63
- "Origin: #{origin}\r\n" +
64
- "Sec-WebSocket-Key1: #{key1}\r\n" +
65
- "Sec-WebSocket-Key2: #{key2}\r\n" +
66
- "\r\n" +
67
- "#{key3}")
68
- flush()
69
-
70
- line = gets().chomp()
71
- raise(WebSocket::Error, "bad response: #{line}") if !(line =~ /\AHTTP\/1.1 101 /n)
72
- read_header()
73
- if (@header["sec-websocket-origin"] || "").downcase() != origin.downcase()
74
- raise(WebSocket::Error,
75
- "origin doesn't match: '#{@header["sec-websocket-origin"]}' != '#{origin}'")
76
- end
77
- reply_digest = read(16)
78
- expected_digest = hixie_76_security_digest(key1, key2, key3)
79
- if reply_digest != expected_digest
80
- raise(WebSocket::Error,
81
- "security digest doesn't match: %p != %p" % [reply_digest, expected_digest])
82
- end
83
- @handshaked = true
84
-
85
- @received = []
86
- @buffer = ""
87
- @closing_started = false
88
-
89
- if block_given?
90
- @rth = Thread.new do
91
- while data = receive()
92
- yield data
93
- end
94
- end
95
- end
96
-
97
- end
98
-
99
- attr_reader(:server, :header, :path)
100
-
101
- def handshake(status = nil, header = {})
102
- if @handshaked
103
- raise(WebSocket::Error, "handshake has already been done")
104
- end
105
- status ||= "101 Switching Protocols"
106
- def_header = {}
107
- case @web_socket_version
108
- when "hixie-75"
109
- def_header["WebSocket-Origin"] = self.origin
110
- def_header["WebSocket-Location"] = self.location
111
- extra_bytes = ""
112
- when "hixie-76"
113
- def_header["Sec-WebSocket-Origin"] = self.origin
114
- def_header["Sec-WebSocket-Location"] = self.location
115
- extra_bytes = hixie_76_security_digest(
116
- @header["Sec-WebSocket-Key1"], @header["Sec-WebSocket-Key2"], @key3)
117
- else
118
- def_header["Sec-WebSocket-Accept"] = security_digest(@header["sec-websocket-key"])
119
- extra_bytes = ""
120
- end
121
- header = def_header.merge(header)
122
- header_str = header.map(){ |k, v| "#{k}: #{v}\r\n" }.join("")
123
- # Note that Upgrade and Connection must appear in this order.
124
- write(
125
- "HTTP/1.1 #{status}\r\n" +
126
- "Upgrade: websocket\r\n" +
127
- "Connection: Upgrade\r\n" +
128
- "#{header_str}\r\n#{extra_bytes}")
129
- flush()
130
- @handshaked = true
131
- end
132
-
133
- def send(data)
134
- if !@handshaked
135
- raise(WebSocket::Error, "call WebSocket\#handshake first")
136
- end
137
- case @web_socket_version
138
- when "hixie-75", "hixie-76"
139
- data = force_encoding(data.dup(), "ASCII-8BIT")
140
- write("\x00#{data}\xff")
141
- flush()
142
- else
143
- send_frame(OPCODE_TEXT, data, !@server)
144
- end
145
- end
146
-
147
- def receive()
148
- if !@handshaked
149
- raise(WebSocket::Error, "call WebSocket\#handshake first")
150
- end
151
- case @web_socket_version
152
-
153
- when "hixie-75", "hixie-76"
154
- packet = gets("\xff")
155
- return nil if !packet
156
- if packet =~ /\A\x00(.*)\xff\z/nm
157
- return force_encoding($1, "UTF-8")
158
- elsif packet == "\xff" && read(1) == "\x00" # closing
159
- close(1005, "", :peer)
160
- return nil
161
- else
162
- raise(WebSocket::Error, "input must be either '\\x00...\\xff' or '\\xff\\x00'")
163
- end
164
-
165
- else
166
- begin
167
- bytes = read(2).unpack("C*")
168
- fin = (bytes[0] & 0x80) != 0
169
- opcode = bytes[0] & 0x0f
170
- mask = (bytes[1] & 0x80) != 0
171
- plength = bytes[1] & 0x7f
172
- if plength == 126
173
- bytes = read(2)
174
- plength = bytes.unpack("n")[0]
175
- elsif plength == 127
176
- bytes = read(8)
177
- (high, low) = bytes.unpack("NN")
178
- plength = high * (2 ** 32) + low
179
- end
180
- if @server && !mask
181
- # Masking is required.
182
- @socket.close()
183
- raise(WebSocket::Error, "received unmasked data")
184
- end
185
- mask_key = mask ? read(4).unpack("C*") : nil
186
- payload = read(plength)
187
- payload = apply_mask(payload, mask_key) if mask
188
- case opcode
189
- when OPCODE_TEXT
190
- return force_encoding(payload, "UTF-8")
191
- when OPCODE_BINARY
192
- raise(WebSocket::Error, "received binary data, which is not supported")
193
- when OPCODE_CLOSE
194
- close(1005, "", :peer)
195
- return nil
196
- when OPCODE_PING
197
- raise(WebSocket::Error, "received ping, which is not supported")
198
- when OPCODE_PONG
199
- else
200
- raise(WebSocket::Error, "received unknown opcode: %d" % opcode)
201
- end
202
- rescue EOFError
203
- return nil
204
- end
205
-
206
- end
207
- end
208
-
209
- def tcp_socket
210
- return @socket
211
- end
212
-
213
- def host
214
- return @header["host"]
215
- end
216
-
217
- def origin
218
- case @web_socket_version
219
- when "7", "8"
220
- name = "sec-websocket-origin"
221
- else
222
- name = "origin"
223
- end
224
- if @header[name]
225
- return @header[name]
226
- else
227
- raise(WebSocket::Error, "%s header is missing" % name)
228
- end
229
- end
230
-
231
- def location
232
- return "ws://#{self.host}#{@path}"
233
- end
234
-
235
- # Does closing handshake.
236
- def close(code = 1005, reason = "", origin = :self)
237
- if !@closing_started
238
- case @web_socket_version
239
- when "hixie-75", "hixie-76"
240
- write("\xff\x00")
241
- else
242
- if code == 1005
243
- payload = ""
244
- else
245
- payload = [code].pack("n") + force_encoding(reason.dup(), "ASCII-8BIT")
246
- end
247
- send_frame(OPCODE_CLOSE, payload, false)
248
- end
249
- end
250
- @socket.close() if origin == :peer
251
- @closing_started = true
252
- end
253
-
254
- def close_socket()
255
- @socket.close()
256
- end
257
-
258
- private
259
-
260
- NOISE_CHARS = ("\x21".."\x2f").to_a() + ("\x3a".."\x7e").to_a()
261
-
262
- def read_header()
263
- @header = {}
264
- while line = gets()
265
- line = line.chomp()
266
- break if line.empty?
267
- if !(line =~ /\A(\S+): (.*)\z/n)
268
- raise(WebSocket::Error, "invalid request: #{line}")
269
- end
270
- @header[$1] = $2
271
- @header[$1.downcase()] = $2
272
- end
273
- if !@header["upgrade"]
274
- raise(WebSocket::Error, "Upgrade header is missing")
275
- end
276
- if !(@header["upgrade"] =~ /\AWebSocket\z/i)
277
- raise(WebSocket::Error, "invalid Upgrade: " + @header["upgrade"])
278
- end
279
- if !@header["connection"]
280
- raise(WebSocket::Error, "Connection header is missing")
281
- end
282
- if @header["connection"].split(/,/).grep(/\A\s*Upgrade\s*\z/i).empty?
283
- raise(WebSocket::Error, "invalid Connection: " + @header["connection"])
284
- end
285
- end
286
-
287
- def send_frame(opcode, payload, mask)
288
- payload = force_encoding(payload.dup(), "ASCII-8BIT")
289
- # Setting StringIO's encoding to ASCII-8BIT.
290
- buffer = StringIO.new(force_encoding("", "ASCII-8BIT"))
291
- write_byte(buffer, 0x80 | opcode)
292
- masked_byte = mask ? 0x80 : 0x00
293
- if payload.bytesize <= 125
294
- write_byte(buffer, masked_byte | payload.bytesize)
295
- elsif payload.bytesize < 2 ** 16
296
- write_byte(buffer, masked_byte | 126)
297
- buffer.write([payload.bytesize].pack("n"))
298
- else
299
- write_byte(buffer, masked_byte | 127)
300
- buffer.write([payload.bytesize / (2 ** 32), payload.bytesize % (2 ** 32)].pack("NN"))
301
- end
302
- if mask
303
- mask_key = Array.new(4){ rand(256) }
304
- buffer.write(mask_key.pack("C*"))
305
- payload = apply_mask(payload, mask_key)
306
- end
307
- buffer.write(payload)
308
- write(buffer.string)
309
- end
310
-
311
- def gets(rs = $/)
312
- line = @socket.gets(rs)
313
- $stderr.printf("recv> %p\n", line) if WebSocket.debug
314
- return line
315
- end
316
-
317
- def read(num_bytes)
318
- str = @socket.read(num_bytes)
319
- $stderr.printf("recv> %p\n", str) if WebSocket.debug
320
- if str && str.bytesize == num_bytes
321
- return str
322
- else
323
- raise(EOFError)
324
- end
325
- end
326
-
327
- def write(data)
328
- if WebSocket.debug
329
- data.scan(/\G(.*?(\n|\z))/n) do
330
- $stderr.printf("send> %p\n", $&) if !$&.empty?
331
- end
332
- end
333
- @socket.write(data)
334
- end
335
-
336
- def flush()
337
- @socket.flush()
338
- end
339
-
340
- def write_byte(buffer, byte)
341
- buffer.write([byte].pack("C"))
342
- end
343
-
344
- def security_digest(key)
345
- return Base64.encode64(Digest::SHA1.digest(key + WEB_SOCKET_GUID)).gsub(/\n/, "")
346
- end
347
-
348
- def hixie_76_security_digest(key1, key2, key3)
349
- bytes1 = websocket_key_to_bytes(key1)
350
- bytes2 = websocket_key_to_bytes(key2)
351
- return Digest::MD5.digest(bytes1 + bytes2 + key3)
352
- end
353
-
354
- def apply_mask(payload, mask_key)
355
- orig_bytes = payload.unpack("C*")
356
- new_bytes = []
357
- orig_bytes.each_with_index() do |b, i|
358
- new_bytes.push(b ^ mask_key[i % 4])
359
- end
360
- return new_bytes.pack("C*")
361
- end
362
-
363
- def generate_key()
364
- spaces = 1 + rand(12)
365
- max = 0xffffffff / spaces
366
- number = rand(max + 1)
367
- key = (number * spaces).to_s()
368
- (1 + rand(12)).times() do
369
- char = NOISE_CHARS[rand(NOISE_CHARS.size)]
370
- pos = rand(key.size + 1)
371
- key[pos...pos] = char
372
- end
373
- spaces.times() do
374
- pos = 1 + rand(key.size - 1)
375
- key[pos...pos] = " "
376
- end
377
- return key
378
- end
379
-
380
- def generate_key3()
381
- return [rand(0x100000000)].pack("N") + [rand(0x100000000)].pack("N")
382
- end
383
-
384
- def websocket_key_to_bytes(key)
385
- num = key.gsub(/[^\d]/n, "").to_i() / key.scan(/ /).size
386
- return [num].pack("N")
387
- end
388
-
389
- def force_encoding(str, encoding)
390
- if str.respond_to?(:force_encoding)
391
- return str.force_encoding(encoding)
392
- else
393
- return str
394
- end
395
- end
396
-
397
- def ssl_handshake(socket)
398
- ssl_context = OpenSSL::SSL::SSLContext.new()
399
- ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
400
- ssl_socket.sync_close = true
401
- ssl_socket.connect()
402
- return ssl_socket
403
- end
404
-
405
- end