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