simpleblockingwebsocketclient 0.42 → 0.43

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.
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