libwebsocket 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -0
- data/README.md +3 -3
- data/Rakefile +4 -20
- data/examples/eventmachine_server.rb +1 -1
- data/examples/plain_client.rb +1 -1
- data/examples/thin_server.rb +1 -1
- data/lib/libwebsocket.rb +8 -9
- data/lib/libwebsocket/cookie.rb +1 -1
- data/lib/libwebsocket/cookie/request.rb +4 -4
- data/lib/libwebsocket/frame.rb +155 -28
- data/lib/libwebsocket/frame/error.rb +99 -0
- data/lib/libwebsocket/message.rb +3 -1
- data/lib/libwebsocket/version.rb +3 -0
- data/libwebsocket.gemspec +16 -66
- data/test/libwebsocket/{test_frame.rb → draft-ietf-hybi-00/test_frame.rb} +25 -19
- data/test/libwebsocket/draft-ietf-hybi-10/test_frame.rb +84 -0
- data/test/libwebsocket/{opening_handshake → handshake}/test_client.rb +0 -0
- data/test/libwebsocket/{opening_handshake → handshake}/test_server.rb +0 -0
- data/test/libwebsocket/test_cookie.rb +1 -0
- data/test/libwebsocket/test_url.rb +6 -0
- metadata +61 -12
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# LibWebSocket [![](http://travis-ci.org/imanel/libwebsocket.png)](http://travis-ci.org/imanel/libwebsocket)
|
2
2
|
|
3
3
|
A WebSocket message parser/constructor. It is not a server and is not meant to
|
4
4
|
be one. It can be used in any server, event loop etc.
|
@@ -80,9 +80,9 @@ examples directory in the repository.
|
|
80
80
|
|
81
81
|
## Copyright
|
82
82
|
|
83
|
-
Copyright (C)
|
83
|
+
Copyright (C) 2012, Bernard Potocki.
|
84
84
|
|
85
85
|
Based on protocol-websocket perl distribution by Viacheslav Tykhanovskyi.
|
86
86
|
|
87
87
|
This program is free software, you can redistribute it and/or modify it under
|
88
|
-
the MIT License.
|
88
|
+
the MIT License.
|
data/Rakefile
CHANGED
@@ -1,27 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'lib/libwebsocket'
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
4
3
|
|
5
|
-
|
4
|
+
require 'rake/testtask'
|
6
5
|
|
7
6
|
Rake::TestTask.new do |t|
|
8
7
|
t.libs << "test"
|
9
8
|
t.test_files = FileList['test/**/test_*.rb']
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
require 'jeweler'
|
14
|
-
Jeweler::Tasks.new do |gemspec|
|
15
|
-
gemspec.name = "libwebsocket"
|
16
|
-
gemspec.version = LibWebSocket::VERSION
|
17
|
-
gemspec.summary = "Universal Ruby library to handle WebSocket protocol"
|
18
|
-
gemspec.description = "Universal Ruby library to handle WebSocket protocol"
|
19
|
-
gemspec.email = "bernard.potocki@imanel.org"
|
20
|
-
gemspec.homepage = "http://github.com/imanel/libwebsocket"
|
21
|
-
gemspec.authors = ["Bernard Potocki"]
|
22
|
-
gemspec.files.exclude ".gitignore"
|
23
|
-
gemspec.add_dependency "addressable"
|
24
|
-
end
|
25
|
-
rescue LoadError
|
26
|
-
puts "Jeweler not available. Install it with: gem install jeweler"
|
27
|
-
end
|
11
|
+
task :default => :test
|
data/examples/plain_client.rb
CHANGED
data/examples/thin_server.rb
CHANGED
data/lib/libwebsocket.rb
CHANGED
@@ -2,16 +2,15 @@
|
|
2
2
|
# not provide a WebSocket server or client, but is made for using in http servers
|
3
3
|
# or clients to provide WebSocket support.
|
4
4
|
module LibWebSocket
|
5
|
+
class Error < RuntimeError; end # Universal LibWebSocket error class
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
autoload :Cookie, "#{File.dirname(__FILE__)}/libwebsocket/cookie"
|
9
|
-
autoload :Frame, "#{File.dirname(__FILE__)}/libwebsocket/frame"
|
7
|
+
autoload :Cookie, "#{File.dirname(__FILE__)}/libwebsocket/cookie"
|
8
|
+
autoload :Frame, "#{File.dirname(__FILE__)}/libwebsocket/frame"
|
10
9
|
autoload :OpeningHandshake, "#{File.dirname(__FILE__)}/libwebsocket/opening_handshake"
|
11
|
-
autoload :Message,
|
12
|
-
autoload :Request,
|
13
|
-
autoload :Response,
|
14
|
-
autoload :Stateful,
|
15
|
-
autoload :URL,
|
10
|
+
autoload :Message, "#{File.dirname(__FILE__)}/libwebsocket/message"
|
11
|
+
autoload :Request, "#{File.dirname(__FILE__)}/libwebsocket/request"
|
12
|
+
autoload :Response, "#{File.dirname(__FILE__)}/libwebsocket/response"
|
13
|
+
autoload :Stateful, "#{File.dirname(__FILE__)}/libwebsocket/stateful"
|
14
|
+
autoload :URL, "#{File.dirname(__FILE__)}/libwebsocket/url"
|
16
15
|
|
17
16
|
end
|
data/lib/libwebsocket/cookie.rb
CHANGED
@@ -20,12 +20,12 @@ module LibWebSocket
|
|
20
20
|
|
21
21
|
cookie = nil
|
22
22
|
self.pairs.each do |pair|
|
23
|
-
next
|
23
|
+
next unless pair[0]
|
24
24
|
|
25
25
|
if pair[0].match(/^[^\$]/)
|
26
|
-
cookies.push(cookie)
|
26
|
+
cookies.push(cookie) if cookie
|
27
27
|
|
28
|
-
cookie = self.build_cookie(
|
28
|
+
cookie = self.build_cookie(:name => pair[0], :value => pair[1], :version => version)
|
29
29
|
elsif pair[0] == '$Path'
|
30
30
|
cookie.path = pair[1]
|
31
31
|
elsif pair[0] == '$Domain'
|
@@ -33,7 +33,7 @@ module LibWebSocket
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
cookies.push(cookie)
|
36
|
+
cookies.push(cookie) if cookie
|
37
37
|
|
38
38
|
return cookies
|
39
39
|
end
|
data/lib/libwebsocket/frame.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
1
3
|
module LibWebSocket
|
2
4
|
# Construct or parse a WebSocket frame.
|
3
5
|
#
|
@@ -5,22 +7,50 @@ module LibWebSocket
|
|
5
7
|
#
|
6
8
|
# # Create frame
|
7
9
|
# frame = LibWebSocket::Frame.new('123')
|
8
|
-
# frame.
|
10
|
+
# frame.to_bytes
|
9
11
|
#
|
10
12
|
# # Parse frames
|
11
13
|
# frame = LibWebSocket::Frame.new
|
12
|
-
# frame.append(
|
13
|
-
# frame.next #
|
14
|
-
# frame.next #
|
14
|
+
# frame.append(...)
|
15
|
+
# frame.next # get next message
|
16
|
+
# frame.next # get another next message
|
15
17
|
class Frame
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
autoload :Error, "#{File.dirname(__FILE__)}/frame/error"
|
20
|
+
|
21
|
+
MAX_RAND_INT = 2 ** 32
|
22
|
+
TYPES = {
|
23
|
+
:text => 0x01,
|
24
|
+
:binary => 0x02,
|
25
|
+
:ping => 0x09,
|
26
|
+
:pong => 0x0a,
|
27
|
+
:close => 0x08
|
28
|
+
}
|
29
|
+
|
30
|
+
attr_accessor :buffer, :version, :max_fragments_amount, :max_payload_size, :fin, :rsv, :opcode, :masked
|
20
31
|
|
21
|
-
# Create new
|
22
|
-
|
23
|
-
|
32
|
+
# Create a new Frame instance. Automatically detect if the passed data is a string or bytes.
|
33
|
+
# Options can be buffer or hash with options:
|
34
|
+
# :buffer - content of buffer
|
35
|
+
# :type - frame type(allowed values: text, binary, ping, pong, close)
|
36
|
+
# :version - protocol version(see readme for supported versions)
|
37
|
+
# :max_fragments_amount - max number of message parts per single frame
|
38
|
+
# :max_payload_size - max bytesize of single message
|
39
|
+
# @example
|
40
|
+
# LibWebSocket::Frame->new('data')
|
41
|
+
# LibWebSocket::Frame->new(:buffer => 'data', :type => 'close')
|
42
|
+
def initialize(options = '')
|
43
|
+
if options.is_a?(Hash)
|
44
|
+
options.each {|k,v| instance_variable_set("@#{k}",v) }
|
45
|
+
else
|
46
|
+
@buffer = options
|
47
|
+
end
|
48
|
+
|
49
|
+
@buffer ||= ''
|
50
|
+
@version ||= 'draft-ietf-hybi-10'
|
51
|
+
@fragments = []
|
52
|
+
@max_fragments_amount ||= 128
|
53
|
+
@max_payload_size ||= 65536
|
24
54
|
end
|
25
55
|
|
26
56
|
# Append a frame chunk.
|
@@ -30,37 +60,134 @@ module LibWebSocket
|
|
30
60
|
def append(string = nil)
|
31
61
|
return unless string.is_a?(String)
|
32
62
|
|
33
|
-
|
63
|
+
string.force_encoding("ASCII-8BIT") if string.respond_to?(:force_encoding)
|
34
64
|
|
65
|
+
self.buffer += string
|
35
66
|
return self
|
36
67
|
end
|
37
68
|
|
38
69
|
# Return the next frame.
|
39
70
|
# @example
|
40
|
-
# frame.append(
|
41
|
-
# frame.
|
42
|
-
#
|
43
|
-
# frame.next; # => foo
|
44
|
-
# frame.next; # => bar
|
71
|
+
# frame.append(...)
|
72
|
+
# frame.next; # next message
|
45
73
|
def next
|
46
|
-
|
74
|
+
bytes = self.next_bytes
|
75
|
+
return unless bytes
|
47
76
|
|
48
|
-
|
49
|
-
|
77
|
+
bytes.force_encoding('UTF-8') if bytes.respond_to?(:force_encoding)
|
78
|
+
return bytes
|
79
|
+
end
|
50
80
|
|
51
|
-
|
81
|
+
def opcode
|
82
|
+
@opcode || 1
|
52
83
|
end
|
53
84
|
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
|
58
|
-
def
|
59
|
-
|
85
|
+
def ping?; opcode == TYPES[:ping]; end # Check if frame is a ping request.
|
86
|
+
def pong?; opcode == TYPES[:pong]; end # Check if frame is a pong response.
|
87
|
+
def close?; opcode == TYPES[:close]; end # Check if frame is of close type.
|
88
|
+
def text?; opcode == TYPES[:text]; end # Check if frame is of text type.
|
89
|
+
def binary?; opcode == TYPES[:binary]; end # Check if frame is of binary type.
|
90
|
+
|
91
|
+
# Return the next message as a UTF-8 encoded string.
|
92
|
+
def next_bytes
|
93
|
+
if ['draft-hixie-75', 'draft-ietf-hybi-00'].include? self.version
|
94
|
+
if self.buffer.slice!(/\A\xff\x00/m)
|
95
|
+
self.opcode = TYPES[:close]
|
96
|
+
return ''
|
97
|
+
end
|
98
|
+
|
99
|
+
return unless self.buffer.slice!(/^[^\x00]*\x00(.*?)\xff/m)
|
100
|
+
return $1
|
101
|
+
end
|
102
|
+
|
103
|
+
return unless self.buffer.length >= 2
|
104
|
+
|
105
|
+
while self.buffer.length > 0
|
106
|
+
hdr = self.buffer[0..0]
|
107
|
+
bits = hdr.unpack("B*").first.split(//)
|
108
|
+
|
109
|
+
self.fin = bits[0]
|
110
|
+
self.rsv = bits[1..3]
|
111
|
+
opcode = hdr.unpack('C').first & 0b00001111
|
112
|
+
offset = 1 # FIN,RSV[1-3],OPCODE
|
113
|
+
|
114
|
+
payload_len = buffer[1..1].unpack('C').first
|
115
|
+
self.masked = (payload_len & 0b10000000) >> 7
|
116
|
+
offset += 1 # + MASKED,PAYLOAD_LEN
|
117
|
+
|
118
|
+
payload_len = payload_len & 0b01111111
|
119
|
+
if payload_len == 126
|
120
|
+
return unless self.buffer.length >= offset + 2
|
121
|
+
|
122
|
+
payload_len = self.buffer[offset..offset+2].unpack('n').first
|
123
|
+
offset += 2
|
124
|
+
elsif payload_len > 126
|
125
|
+
return unless self.buffer.length >= offset + 4
|
126
|
+
bits = self.buffer[offset..offset+7].unpack('B*').first
|
127
|
+
bits.gsub!(/^./,'0') # Most significant bit must be 0.
|
128
|
+
bits = bits[32..-1] # No idea how to unpack 64-bit unsigned integer with big-endian byte order
|
129
|
+
payload_len = Array(bits).pack("B*").unpack("N").first
|
130
|
+
offset += 8
|
131
|
+
end
|
132
|
+
|
133
|
+
if payload_len > self.max_payload_size
|
134
|
+
self.buffer = ''
|
135
|
+
raise Error::MessageTooBig.new("Payload is too big. Deny big message (#{payload_len}) or increase max_payload_size (#{self.max_payload_size})")
|
136
|
+
end
|
137
|
+
|
138
|
+
mask = ''
|
139
|
+
if self.masked == 1
|
140
|
+
return unless self.buffer.length >= offset + 4
|
141
|
+
|
142
|
+
mask = self.buffer[offset..offset+4]
|
143
|
+
offset += 4
|
144
|
+
end
|
145
|
+
|
146
|
+
return if self.buffer.length < offset + payload_len
|
147
|
+
|
148
|
+
payload = self.buffer[offset..offset+payload_len-1]
|
149
|
+
payload = self.mask(payload, mask) if self.masked == 1
|
150
|
+
|
151
|
+
self.buffer[0..offset+payload_len-1] = ''
|
152
|
+
|
153
|
+
# Inject control frame
|
154
|
+
if !@fragments.empty? && (opcode & 0b1000 != 0)
|
155
|
+
self.opcode = opcode
|
156
|
+
return payload
|
157
|
+
end
|
158
|
+
|
159
|
+
if self.fin != '0'
|
160
|
+
if @fragments.empty?
|
161
|
+
self.opcode = opcode
|
162
|
+
else
|
163
|
+
self.opcode = @fragments.shift
|
164
|
+
end
|
165
|
+
|
166
|
+
payload = (@fragments + Array(payload)).join
|
167
|
+
@fragments = []
|
168
|
+
return payload
|
169
|
+
else
|
170
|
+
# Remember first fragment opcode
|
171
|
+
@fragments.push(opcode) if @fragments.empty?
|
172
|
+
|
173
|
+
@fragments.push(payload)
|
174
|
+
raise Error::PolicyViolation.new("Too many fragments") if @fragments.size > self.max_fragments_amount
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
return
|
179
|
+
end
|
60
180
|
|
61
|
-
|
181
|
+
protected
|
62
182
|
|
63
|
-
|
183
|
+
def mask(payload, mask)
|
184
|
+
mask = mask.bytes.to_a
|
185
|
+
payload = payload.bytes.to_a
|
186
|
+
payload.each_with_index do |p, i|
|
187
|
+
j = i % 4
|
188
|
+
payload[i] = p ^ mask[j]
|
189
|
+
end
|
190
|
+
return payload.collect(&:chr).join
|
64
191
|
end
|
65
192
|
|
66
193
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module LibWebSocket
|
2
|
+
class Frame
|
3
|
+
|
4
|
+
# Generic close error command for frame. Should not be called directly - use one of subclasses.
|
5
|
+
# This should be catched and responded to according to clean_close? and status.
|
6
|
+
class Error < LibWebSocket::Error
|
7
|
+
|
8
|
+
# Should close frame be sent before closing?
|
9
|
+
# If so then status code should be used to build it.
|
10
|
+
def clean_close?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
# Status code for closing frame.
|
15
|
+
def status_code
|
16
|
+
500 # This should not be called directly
|
17
|
+
end
|
18
|
+
|
19
|
+
# 1000 indicates a normal closure, meaning whatever purpose the
|
20
|
+
# connection was established for has been fulfilled.
|
21
|
+
class NormalClosure < Error
|
22
|
+
def status_code; 1000; end
|
23
|
+
end
|
24
|
+
|
25
|
+
# 1001 indicates that an endpoint is "going away", such as a server
|
26
|
+
# going down, or a browser having navigated away from a page.
|
27
|
+
class GoingAway < Error
|
28
|
+
def status_code; 1001; end
|
29
|
+
end
|
30
|
+
|
31
|
+
# 1002 indicates that an endpoint is terminating the connection due
|
32
|
+
# to a protocol error.
|
33
|
+
class ProtocolError < Error
|
34
|
+
def status_code; 1002; end
|
35
|
+
end
|
36
|
+
|
37
|
+
# 1003 indicates that an endpoint is terminating the connection
|
38
|
+
# because it has received a type of data it cannot accept (e.g. an
|
39
|
+
# endpoint that understands only text data MAY send this if it
|
40
|
+
# receives a binary message).
|
41
|
+
class UnsupportedData < Error
|
42
|
+
def status_code; 1003; end
|
43
|
+
end
|
44
|
+
|
45
|
+
# 1005 is a reserved value and MUST NOT be set as a status code in a
|
46
|
+
# Close control frame by an endpoint. It is designated for use in
|
47
|
+
# applications expecting a status code to indicate that no status
|
48
|
+
# code was actually present.
|
49
|
+
class NoStatusRcvd < Error
|
50
|
+
def status_code; 1005; end
|
51
|
+
end
|
52
|
+
|
53
|
+
# 1006 is a reserved value and MUST NOT be set as a status code in a
|
54
|
+
# Close control frame by an endpoint. It is designated for use in
|
55
|
+
# applications expecting a status code to indicate that the
|
56
|
+
# connection was closed abnormally, e.g. without sending or
|
57
|
+
# receiving a Close control frame.
|
58
|
+
class AbnormalClosure < Error
|
59
|
+
def status_code; 1006; end
|
60
|
+
end
|
61
|
+
|
62
|
+
# 1007 indicates that an endpoint is terminating the connection
|
63
|
+
# because it has received data within a message that was not
|
64
|
+
# consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
|
65
|
+
# data within a text message).
|
66
|
+
class InvalidFramePayloadData < Error
|
67
|
+
def status_code; 1007; end
|
68
|
+
end
|
69
|
+
|
70
|
+
# 1008 indicates that an endpoint is terminating the connection
|
71
|
+
# because it has received a message that violates its policy. This
|
72
|
+
# is a generic status code that can be returned when there is no
|
73
|
+
# other more suitable status code (e.g. 1003 or 1009), or if there
|
74
|
+
# is a need to hide specific details about the policy.
|
75
|
+
class PolicyViolation < Error
|
76
|
+
def status_code; 1008; end
|
77
|
+
end
|
78
|
+
|
79
|
+
# 1009 indicates that an endpoint is terminating the connection
|
80
|
+
# because it has received a message which is too big for it to
|
81
|
+
# process.
|
82
|
+
class MessageTooBig < Error
|
83
|
+
def status_code; 1009; end
|
84
|
+
end
|
85
|
+
|
86
|
+
# 1010 indicates that an endpoint (client) is terminating the
|
87
|
+
# connection because it has expected the server to negotiate one or
|
88
|
+
# more extension, but the server didn't return them in the response
|
89
|
+
# message of the WebSocket handshake. The list of extensions which
|
90
|
+
# are needed SHOULD appear in the /reason/ part of the Close frame.
|
91
|
+
# Note that this status code is not used by the server, because it
|
92
|
+
# can fail the WebSocket handshake instead.
|
93
|
+
class MandatoryExtension < Error
|
94
|
+
def status_code; 1010; end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/libwebsocket/message.rb
CHANGED
@@ -126,7 +126,9 @@ module LibWebSocket
|
|
126
126
|
def append(data)
|
127
127
|
return if self.error
|
128
128
|
|
129
|
-
|
129
|
+
data = data.encode("ASCII-8BIT") if data.respond_to?(:encode)
|
130
|
+
|
131
|
+
@buffer << data
|
130
132
|
|
131
133
|
if @buffer.length > @max_message_size
|
132
134
|
self.error = 'Message is too long'
|
data/libwebsocket.gemspec
CHANGED
@@ -1,72 +1,22 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "libwebsocket/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |s|
|
7
|
-
s.name
|
8
|
-
s.version
|
6
|
+
s.name = "libwebsocket"
|
7
|
+
s.version = LibWebSocket::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Bernard Potocki"]
|
10
|
+
s.email = ["bernard.potocki@imanel.org"]
|
11
|
+
s.homepage = "http://github.com/imanel/libwebsocket"
|
12
|
+
s.summary = %q{Universal Ruby library to handle WebSocket protocol}
|
13
|
+
s.description = %q{Universal Ruby library to handle WebSocket protocol}
|
9
14
|
|
10
|
-
s.
|
11
|
-
s.
|
12
|
-
s.date = "2012-03-20"
|
13
|
-
s.description = "Universal Ruby library to handle WebSocket protocol"
|
14
|
-
s.email = "bernard.potocki@imanel.org"
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"README.md"
|
17
|
-
]
|
18
|
-
s.files = [
|
19
|
-
"CHANGELOG.md",
|
20
|
-
"README.md",
|
21
|
-
"Rakefile",
|
22
|
-
"examples/eventmachine_server.rb",
|
23
|
-
"examples/plain_client.rb",
|
24
|
-
"examples/thin_server.rb",
|
25
|
-
"lib/libwebsocket.rb",
|
26
|
-
"lib/libwebsocket/cookie.rb",
|
27
|
-
"lib/libwebsocket/cookie/request.rb",
|
28
|
-
"lib/libwebsocket/cookie/response.rb",
|
29
|
-
"lib/libwebsocket/frame.rb",
|
30
|
-
"lib/libwebsocket/message.rb",
|
31
|
-
"lib/libwebsocket/opening_handshake.rb",
|
32
|
-
"lib/libwebsocket/opening_handshake/client.rb",
|
33
|
-
"lib/libwebsocket/opening_handshake/server.rb",
|
34
|
-
"lib/libwebsocket/request.rb",
|
35
|
-
"lib/libwebsocket/response.rb",
|
36
|
-
"lib/libwebsocket/stateful.rb",
|
37
|
-
"lib/libwebsocket/url.rb",
|
38
|
-
"libwebsocket.gemspec",
|
39
|
-
"test/libwebsocket/cookie/request.rb",
|
40
|
-
"test/libwebsocket/cookie/response.rb",
|
41
|
-
"test/libwebsocket/opening_handshake/test_client.rb",
|
42
|
-
"test/libwebsocket/opening_handshake/test_server.rb",
|
43
|
-
"test/libwebsocket/test_cookie.rb",
|
44
|
-
"test/libwebsocket/test_frame.rb",
|
45
|
-
"test/libwebsocket/test_message.rb",
|
46
|
-
"test/libwebsocket/test_request_75.rb",
|
47
|
-
"test/libwebsocket/test_request_76.rb",
|
48
|
-
"test/libwebsocket/test_request_common.rb",
|
49
|
-
"test/libwebsocket/test_response_75.rb",
|
50
|
-
"test/libwebsocket/test_response_76.rb",
|
51
|
-
"test/libwebsocket/test_response_common.rb",
|
52
|
-
"test/libwebsocket/test_url.rb",
|
53
|
-
"test/test_helper.rb"
|
54
|
-
]
|
55
|
-
s.homepage = "http://github.com/imanel/libwebsocket"
|
56
|
-
s.require_paths = ["lib"]
|
57
|
-
s.rubygems_version = "1.8.11"
|
58
|
-
s.summary = "Universal Ruby library to handle WebSocket protocol"
|
59
|
-
|
60
|
-
if s.respond_to? :specification_version then
|
61
|
-
s.specification_version = 3
|
15
|
+
s.add_dependency 'addressable'
|
16
|
+
s.add_development_dependency 'rake'
|
62
17
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
else
|
69
|
-
s.add_dependency(%q<addressable>, [">= 0"])
|
70
|
-
end
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
71
22
|
end
|
72
|
-
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
1
2
|
require 'test_helper'
|
2
3
|
|
3
4
|
class TestFrame < Test::Unit::TestCase
|
4
5
|
|
5
|
-
def
|
6
|
-
f = LibWebSocket::Frame.new
|
6
|
+
def test_append_ietf_hybi_00
|
7
|
+
f = LibWebSocket::Frame.new(:version => 'draft-ietf-hybi-00')
|
7
8
|
|
8
9
|
f.append
|
9
10
|
assert_nil f.next
|
@@ -21,6 +22,11 @@ class TestFrame < Test::Unit::TestCase
|
|
21
22
|
f.append("\xff")
|
22
23
|
assert_equal '', f.next
|
23
24
|
|
25
|
+
f.append("\xff")
|
26
|
+
f.append("\x00")
|
27
|
+
assert_equal '', f.next
|
28
|
+
assert f.close?
|
29
|
+
|
24
30
|
f.append("\x00")
|
25
31
|
assert_nil f.next
|
26
32
|
f.append("foo")
|
@@ -38,28 +44,28 @@ class TestFrame < Test::Unit::TestCase
|
|
38
44
|
assert_nil f.next
|
39
45
|
|
40
46
|
# We append bytes, but read characters
|
41
|
-
msg =
|
47
|
+
msg = '☺'
|
42
48
|
f.append("\x00" + msg + "\xff")
|
43
49
|
msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
|
44
50
|
assert_equal msg, f.next
|
45
51
|
end
|
46
52
|
|
47
|
-
def
|
48
|
-
f = LibWebSocket::Frame.new
|
49
|
-
msg = "\x00\xff"
|
50
|
-
msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
|
51
|
-
assert_equal msg, f.to_s
|
52
|
-
|
53
|
-
f = LibWebSocket::Frame.new('123')
|
54
|
-
msg = "\x00123\xff"
|
55
|
-
msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
|
56
|
-
assert_equal msg, f.to_s
|
57
|
-
|
58
|
-
# We pass characters, but send bytes
|
59
|
-
f = LibWebSocket::Frame.new("\342\230\272")
|
60
|
-
msg = "\x00\342\230\272\xff"
|
61
|
-
msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
|
62
|
-
assert_equal msg, f.to_s
|
53
|
+
def test_new_ietf_hybi_00
|
54
|
+
# f = LibWebSocket::Frame.new(:version => 'draft-ietf-hybi-00')
|
55
|
+
# msg = "\x00\xff"
|
56
|
+
# msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
|
57
|
+
# assert_equal msg, f.to_s
|
58
|
+
#
|
59
|
+
# f = LibWebSocket::Frame.new('123')
|
60
|
+
# msg = "\x00123\xff"
|
61
|
+
# msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
|
62
|
+
# assert_equal msg, f.to_s
|
63
|
+
#
|
64
|
+
# # We pass characters, but send bytes
|
65
|
+
# f = LibWebSocket::Frame.new("\342\230\272")
|
66
|
+
# msg = "\x00\342\230\272\xff"
|
67
|
+
# msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
|
68
|
+
# assert_equal msg, f.to_s
|
63
69
|
end
|
64
70
|
|
65
71
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class TestFrame < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_append_ietf_hybi_10
|
7
|
+
f = LibWebSocket::Frame.new(:version => 'draft-ietf-hybi-10')
|
8
|
+
|
9
|
+
f.append
|
10
|
+
assert_nil f.next
|
11
|
+
f.append('')
|
12
|
+
assert_nil f.next
|
13
|
+
|
14
|
+
# Not masked
|
15
|
+
f.append ["810548656c6c6f"].pack('H*')
|
16
|
+
assert_equal 'Hello', f.next_bytes
|
17
|
+
assert_equal 1, f.opcode
|
18
|
+
assert f.text?
|
19
|
+
|
20
|
+
# Multi
|
21
|
+
f.append(["810548656c6c6f"].pack('H*') + ["810548656c6c6f"].pack('H*'))
|
22
|
+
assert_equal 'Hello', f.next_bytes
|
23
|
+
assert_equal 'Hello', f.next_bytes
|
24
|
+
|
25
|
+
# Masked
|
26
|
+
f.append ["818537fa213d7f9f4d5158"].pack('H*')
|
27
|
+
assert_equal 'Hello', f.next_bytes
|
28
|
+
assert_equal 1, f.opcode
|
29
|
+
|
30
|
+
# Fragments
|
31
|
+
f.append ["010348656c"].pack("H*")
|
32
|
+
assert_nil f.next_bytes
|
33
|
+
f.append ["80026c6f"].pack("H*")
|
34
|
+
assert_equal 'Hello', f.next_bytes
|
35
|
+
assert_equal 1, f.opcode
|
36
|
+
|
37
|
+
# Multi fragments
|
38
|
+
f.append(["010348656c"].pack('H*') + ["80026c6f"].pack('H*'))
|
39
|
+
assert_equal 'Hello', f.next_bytes
|
40
|
+
assert_equal 1, f.opcode
|
41
|
+
|
42
|
+
# Injected control frame (1 fragment, ping, 2 fragment)
|
43
|
+
f.append ["010348656c"].pack('H*')
|
44
|
+
f.append ["890548656c6c6f"].pack('H*')
|
45
|
+
f.append ["80026c6f"].pack('H*')
|
46
|
+
assert_equal 'Hello', f.next_bytes
|
47
|
+
assert_equal 9, f.opcode
|
48
|
+
assert_equal 'Hello', f.next_bytes
|
49
|
+
assert_equal 1, f.opcode
|
50
|
+
|
51
|
+
# Too many fragments
|
52
|
+
130.times { f.append(["010348656c"].pack('H*')) }
|
53
|
+
assert_raise(LibWebSocket::Frame::Error::PolicyViolation) { f.next_bytes }
|
54
|
+
|
55
|
+
# Ping request
|
56
|
+
f = LibWebSocket::Frame.new(:version => 'draft-ietf-hybi-10')
|
57
|
+
f.append ["890548656c6c6f"].pack('H*')
|
58
|
+
assert_equal 'Hello', f.next_bytes
|
59
|
+
assert_equal 9, f.opcode
|
60
|
+
assert f.ping?
|
61
|
+
|
62
|
+
# Ping response
|
63
|
+
f.append ["8a0548656c6c6f"].pack('H*')
|
64
|
+
assert_equal 'Hello', f.next_bytes
|
65
|
+
assert_equal 10, f.opcode
|
66
|
+
assert f.pong?
|
67
|
+
|
68
|
+
# 256 bytes
|
69
|
+
f.append ["827E0100" + '05' * 256].pack('H*')
|
70
|
+
assert_equal 256, f.next_bytes.length
|
71
|
+
assert_equal 2, f.opcode
|
72
|
+
assert f.binary?
|
73
|
+
|
74
|
+
# 64KiB
|
75
|
+
f.append ["827F0000000000010000" + '05' * 65536].pack('H*')
|
76
|
+
assert_equal 65536, f.next_bytes.length
|
77
|
+
assert_equal 2, f.opcode
|
78
|
+
|
79
|
+
# Too big frame
|
80
|
+
f.append ["827F0000000000100000" + '05' * (65536 + 1)].pack('H*')
|
81
|
+
assert_raise(LibWebSocket::Frame::Error::MessageTooBig) { f.next_bytes }
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
File without changes
|
File without changes
|
@@ -4,6 +4,7 @@ class TestCookie < Test::Unit::TestCase
|
|
4
4
|
|
5
5
|
def test_parse
|
6
6
|
cookie = LibWebSocket::Cookie.new
|
7
|
+
assert_nil cookie.parse
|
7
8
|
assert_nil cookie.parse('')
|
8
9
|
assert cookie.parse('foo=bar; baz = zab; hello= "the;re"; here')
|
9
10
|
assert_equal [['foo', 'bar'], ['baz', 'zab'], ['hello', 'the;re'], ['here', nil]], cookie.pairs
|
@@ -3,6 +3,12 @@ require 'test_helper'
|
|
3
3
|
class TestURL < Test::Unit::TestCase
|
4
4
|
|
5
5
|
def test_parse
|
6
|
+
url = LibWebSocket::URL.new
|
7
|
+
assert url.parse('ws://example.com')
|
8
|
+
assert !url.secure
|
9
|
+
assert_equal 'example.com', url.host
|
10
|
+
assert_equal '/', url.resource_name
|
11
|
+
|
6
12
|
url = LibWebSocket::URL.new
|
7
13
|
assert url.parse('ws://example.com/')
|
8
14
|
assert !url.secure
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: libwebsocket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-07-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: addressable
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,15 +21,39 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
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: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
25
46
|
description: Universal Ruby library to handle WebSocket protocol
|
26
|
-
email:
|
47
|
+
email:
|
48
|
+
- bernard.potocki@imanel.org
|
27
49
|
executables: []
|
28
50
|
extensions: []
|
29
|
-
extra_rdoc_files:
|
30
|
-
- README.md
|
51
|
+
extra_rdoc_files: []
|
31
52
|
files:
|
53
|
+
- .gitignore
|
54
|
+
- .travis.yml
|
32
55
|
- CHANGELOG.md
|
56
|
+
- Gemfile
|
33
57
|
- README.md
|
34
58
|
- Rakefile
|
35
59
|
- examples/eventmachine_server.rb
|
@@ -40,6 +64,7 @@ files:
|
|
40
64
|
- lib/libwebsocket/cookie/request.rb
|
41
65
|
- lib/libwebsocket/cookie/response.rb
|
42
66
|
- lib/libwebsocket/frame.rb
|
67
|
+
- lib/libwebsocket/frame/error.rb
|
43
68
|
- lib/libwebsocket/message.rb
|
44
69
|
- lib/libwebsocket/opening_handshake.rb
|
45
70
|
- lib/libwebsocket/opening_handshake/client.rb
|
@@ -48,13 +73,15 @@ files:
|
|
48
73
|
- lib/libwebsocket/response.rb
|
49
74
|
- lib/libwebsocket/stateful.rb
|
50
75
|
- lib/libwebsocket/url.rb
|
76
|
+
- lib/libwebsocket/version.rb
|
51
77
|
- libwebsocket.gemspec
|
52
78
|
- test/libwebsocket/cookie/request.rb
|
53
79
|
- test/libwebsocket/cookie/response.rb
|
54
|
-
- test/libwebsocket/
|
55
|
-
- test/libwebsocket/
|
80
|
+
- test/libwebsocket/draft-ietf-hybi-00/test_frame.rb
|
81
|
+
- test/libwebsocket/draft-ietf-hybi-10/test_frame.rb
|
82
|
+
- test/libwebsocket/handshake/test_client.rb
|
83
|
+
- test/libwebsocket/handshake/test_server.rb
|
56
84
|
- test/libwebsocket/test_cookie.rb
|
57
|
-
- test/libwebsocket/test_frame.rb
|
58
85
|
- test/libwebsocket/test_message.rb
|
59
86
|
- test/libwebsocket/test_request_75.rb
|
60
87
|
- test/libwebsocket/test_request_76.rb
|
@@ -76,16 +103,38 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
103
|
- - ! '>='
|
77
104
|
- !ruby/object:Gem::Version
|
78
105
|
version: '0'
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
hash: -2462651630557065482
|
79
109
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
110
|
none: false
|
81
111
|
requirements:
|
82
112
|
- - ! '>='
|
83
113
|
- !ruby/object:Gem::Version
|
84
114
|
version: '0'
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
hash: -2462651630557065482
|
85
118
|
requirements: []
|
86
119
|
rubyforge_project:
|
87
|
-
rubygems_version: 1.8.
|
120
|
+
rubygems_version: 1.8.24
|
88
121
|
signing_key:
|
89
122
|
specification_version: 3
|
90
123
|
summary: Universal Ruby library to handle WebSocket protocol
|
91
|
-
test_files:
|
124
|
+
test_files:
|
125
|
+
- test/libwebsocket/cookie/request.rb
|
126
|
+
- test/libwebsocket/cookie/response.rb
|
127
|
+
- test/libwebsocket/draft-ietf-hybi-00/test_frame.rb
|
128
|
+
- test/libwebsocket/draft-ietf-hybi-10/test_frame.rb
|
129
|
+
- test/libwebsocket/handshake/test_client.rb
|
130
|
+
- test/libwebsocket/handshake/test_server.rb
|
131
|
+
- test/libwebsocket/test_cookie.rb
|
132
|
+
- test/libwebsocket/test_message.rb
|
133
|
+
- test/libwebsocket/test_request_75.rb
|
134
|
+
- test/libwebsocket/test_request_76.rb
|
135
|
+
- test/libwebsocket/test_request_common.rb
|
136
|
+
- test/libwebsocket/test_response_75.rb
|
137
|
+
- test/libwebsocket/test_response_76.rb
|
138
|
+
- test/libwebsocket/test_response_common.rb
|
139
|
+
- test/libwebsocket/test_url.rb
|
140
|
+
- test/test_helper.rb
|