libwebsocket 0.1.3 → 0.1.4

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.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg/
2
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ script: "bundle exec rake test"
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby
7
+ - rbx
8
+ - ree
9
+
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.4 / 2012-07-13
4
+
5
+ - New gemspec and bundler files
6
+ - Fixed some encoding errors
7
+ - Fixes for JRuby
8
+
3
9
  ## 0.1.3 / 2012-03-21
4
10
 
5
11
  - add "addressable" gem to dependencies
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Protocol::WebSocket
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) 2010, Bernard Potocki.
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
- $:.unshift(File.dirname(__FILE__))
2
- require 'rake/testtask'
3
- require 'lib/libwebsocket'
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
4
3
 
5
- task :default => :test
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
- begin
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
@@ -33,4 +33,4 @@ EventMachine::run do
33
33
  port = 8080
34
34
  EventMachine::start_server host, port, EchoServer
35
35
  puts "Started EchoServer on #{host}:#{port}..."
36
- end
36
+ end
@@ -56,4 +56,4 @@ class WebSocket
56
56
  @socket.close
57
57
  end
58
58
 
59
- end
59
+ end
@@ -66,4 +66,4 @@ Thin::Server.start('127.0.0.1', 8080) do
66
66
  map '/' do
67
67
  run proc{ |env| EchoServer.new.call(env) }
68
68
  end
69
- end
69
+ end
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
- VERSION = '0.1.3' # Version of LibWebSocket
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, "#{File.dirname(__FILE__)}/libwebsocket/message"
12
- autoload :Request, "#{File.dirname(__FILE__)}/libwebsocket/request"
13
- autoload :Response, "#{File.dirname(__FILE__)}/libwebsocket/response"
14
- autoload :Stateful, "#{File.dirname(__FILE__)}/libwebsocket/stateful"
15
- autoload :URL, "#{File.dirname(__FILE__)}/libwebsocket/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
@@ -19,7 +19,7 @@ module LibWebSocket
19
19
  end
20
20
 
21
21
  # Parse cookie string to array
22
- def parse(string)
22
+ def parse(string = nil)
23
23
  self.pairs = []
24
24
 
25
25
  return if string.nil? || string == ''
@@ -20,12 +20,12 @@ module LibWebSocket
20
20
 
21
21
  cookie = nil
22
22
  self.pairs.each do |pair|
23
- next if pair[0].nil?
23
+ next unless pair[0]
24
24
 
25
25
  if pair[0].match(/^[^\$]/)
26
- cookies.push(cookie) unless cookie.nil?
26
+ cookies.push(cookie) if cookie
27
27
 
28
- cookie = self.build_cookie( :name => pair[0], :value => pair[1], :version => version)
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) unless cookie.nil?
36
+ cookies.push(cookie) if cookie
37
37
 
38
38
  return cookies
39
39
  end
@@ -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.to_s # \x00123\xff
10
+ # frame.to_bytes
9
11
  #
10
12
  # # Parse frames
11
13
  # frame = LibWebSocket::Frame.new
12
- # frame.append("123\x00foo\xff56\x00bar\xff789")
13
- # frame.next # => foo
14
- # frame.next # => bar
14
+ # frame.append(...)
15
+ # frame.next # get next message
16
+ # frame.next # get another next message
15
17
  class Frame
16
18
 
17
- def initialize(buffer = nil)
18
- @buffer = buffer || ''
19
- end
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 frame without modification of current
22
- def new(buffer = nil)
23
- self.class.new(buffer)
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
- @buffer += string
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("\x00foo")
41
- # frame.append("\xff\x00bar\xff")
42
- #
43
- # frame.next; # => foo
44
- # frame.next; # => bar
71
+ # frame.append(...)
72
+ # frame.next; # next message
45
73
  def next
46
- return unless @buffer.slice!(/^[^\x00]*\x00(.*?)\xff/m)
74
+ bytes = self.next_bytes
75
+ return unless bytes
47
76
 
48
- string = $1
49
- string.force_encoding('UTF-8') if string.respond_to?(:force_encoding)
77
+ bytes.force_encoding('UTF-8') if bytes.respond_to?(:force_encoding)
78
+ return bytes
79
+ end
50
80
 
51
- return string
81
+ def opcode
82
+ @opcode || 1
52
83
  end
53
84
 
54
- # Construct a WebSocket frame.
55
- # @example
56
- # frame = LibWebSocket::Frame.new('foo')
57
- # frame.to_s # => \x00foo\xff
58
- def to_s
59
- ary = ["\x00", @buffer.dup, "\xff"]
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
- ary.collect{ |s| s.force_encoding('UTF-8') if s.respond_to?(:force_encoding) }
181
+ protected
62
182
 
63
- return ary.join
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
@@ -126,7 +126,9 @@ module LibWebSocket
126
126
  def append(data)
127
127
  return if self.error
128
128
 
129
- @buffer += data
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'
@@ -0,0 +1,3 @@
1
+ module LibWebSocket
2
+ VERSION = '0.1.4'
3
+ end
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 = "libwebsocket"
8
- s.version = "0.1.3"
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.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Bernard Potocki"]
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
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
- s.add_runtime_dependency(%q<addressable>, [">= 0"])
65
- else
66
- s.add_dependency(%q<addressable>, [">= 0"])
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 test_append
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 = "\342\230\272"
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 test_new
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
@@ -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.3
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-03-20 00:00:00.000000000Z
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: &70118587928180 !ruby/object:Gem::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: *70118587928180
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: bernard.potocki@imanel.org
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/opening_handshake/test_client.rb
55
- - test/libwebsocket/opening_handshake/test_server.rb
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.11
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