websocket_parser 0.0.6 → 0.1.0

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/README.md CHANGED
@@ -21,10 +21,10 @@ Or install it yourself as:
21
21
 
22
22
  ## Usage. TMTOWTDI.
23
23
 
24
- ### Use return values
24
+ ### Return values
25
25
 
26
26
  The simplest way to use the websocket parser is to create a new one, fetch
27
- it with data and call the parse methods.
27
+ it with data and query it for new messages.
28
28
 
29
29
  ```ruby
30
30
  require 'websocket_parser'
@@ -33,13 +33,17 @@ parser = WebSocket::Parser.new
33
33
 
34
34
  parser.append data
35
35
 
36
- parser.parse # return next message or nil
37
- parser.parse_all # return all parsed messages
36
+ parser.next_message # return next message or nil
37
+ parser.next_messages # return an array with all parsed messages
38
+
39
+ # To send a message:
40
+
41
+ socket << WebSocket::Message.new('Hi there!').to_data
38
42
 
39
43
  ```
40
44
 
41
- The returned messages are instances of WebSocket::Message and can be either text messages
42
- or control frames. Check WebSocket::Message#type to know the kind of a message.
45
+ Only text or binary messages are returned on the parse methods. To intercept
46
+ control frames use the parser's callbacks.
43
47
 
44
48
  ### Use callbacks
45
49
 
@@ -78,10 +82,6 @@ end
78
82
 
79
83
  parser << socket.read(4096)
80
84
 
81
- # To send a message:
82
-
83
- socket << WebSocket::Message.new('Hi there!').to_data
84
-
85
85
  ```
86
86
 
87
87
  ## Status
@@ -13,11 +13,6 @@ module WebSocket
13
13
  end
14
14
 
15
15
  def valid?
16
- if headers['Connection'].downcase != 'upgrade'
17
- errors << 'Not connection upgrade'
18
- return false
19
- end
20
-
21
16
  if headers['Upgrade'].downcase != 'websocket'
22
17
  errors << 'Connection upgrade is not for websocket'
23
18
  return false
@@ -81,6 +81,13 @@ module WebSocket
81
81
  read_payload if @state == :payload
82
82
 
83
83
  @state == :complete ? process_frame! : nil
84
+
85
+ rescue StandardError => ex
86
+ if @on_error
87
+ @on_error.call(ex.message)
88
+ else
89
+ raise ex
90
+ end
84
91
  end
85
92
 
86
93
  private
@@ -106,9 +113,13 @@ module WebSocket
106
113
 
107
114
  def read_extended_payload_length
108
115
  if message_size == :medium && @data.size >= 2
109
- unpack_bytes(2,'S<')
116
+ size = unpack_bytes(2,'S<')
117
+ ParserError.new("Wrong payload length. Expected to be greater than 125") unless size > 125
118
+ size
110
119
  elsif message_size == :large && @data.size >= 4
111
- unpack_bytes(8,'Q<')
120
+ size = unpack_bytes(8,'Q<')
121
+ ParserError.new("Wrong payload length. Expected to be greater than 65535") unless size > 65_535
122
+ size
112
123
  end
113
124
  end
114
125
 
@@ -137,6 +148,10 @@ module WebSocket
137
148
  @data.slice!(0,num).unpack(format).first
138
149
  end
139
150
 
151
+ def message_frame?
152
+ [:text, :binary].include?(opcode)
153
+ end
154
+
140
155
  def control_frame?
141
156
  [:close, :ping, :pong].include?(opcode)
142
157
  end
@@ -148,9 +163,13 @@ module WebSocket
148
163
  @current_message = @payload
149
164
  end
150
165
 
151
- completed_message = fin? ? @current_message :nil
166
+ completed_message = if fin? && message_frame?
167
+ @current_message
168
+ else
169
+ nil
170
+ end
152
171
 
153
- process_message! if fin?
172
+ fin? ? process_message! : opcode # store the opcode
154
173
 
155
174
  reset_frame!
156
175
 
@@ -160,7 +179,10 @@ module WebSocket
160
179
  def process_message!
161
180
  case opcode
162
181
  when :text
163
- @on_message.call(@current_message.force_encoding("UTF-8")) if @on_message
182
+ msg = @current_message.force_encoding("UTF-8")
183
+ raise ParserError.new('Payload data is not valid UTF-8') unless msg.valid_encoding?
184
+
185
+ @on_message.call(msg) if @on_message
164
186
  when :binary
165
187
  @on_message.call(@current_message) if @on_message
166
188
  when :ping
@@ -173,6 +195,9 @@ module WebSocket
173
195
 
174
196
  @on_close.call(status, message) if @on_close
175
197
  end
198
+
199
+ # Reset message
200
+ @opcode = nil
176
201
  @current_message = nil
177
202
  end
178
203
 
@@ -183,7 +208,12 @@ module WebSocket
183
208
  end
184
209
 
185
210
  def opcode
186
- @opcode ||= OPCODES[@first_byte & 0b00001111]
211
+ @opcode ||= begin
212
+ num = @first_byte & 0b00001111
213
+ opcode = OPCODES[num]
214
+ raise ParserError.new("Unknown opcode #{num}") unless opcode
215
+ opcode
216
+ end
187
217
  end
188
218
 
189
219
  def masked?
@@ -1,3 +1,3 @@
1
1
  module WebSocket
2
- VERSION = "0.0.6"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -9,6 +9,8 @@ require "websocket/parser"
9
9
  module WebSocket
10
10
  extend self
11
11
 
12
+ class WebSocket::ParserError < StandardError; end
13
+
12
14
  PROTOCOL_VERSION = 13 # RFC 6455
13
15
  GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
14
16
  CRLF = "\r\n"
@@ -39,6 +39,13 @@ describe WebSocket::Parser do
39
39
  messages[1].should == 'Made glorious summer by this sun of York'
40
40
  end
41
41
 
42
+ it "does not return control frames" do
43
+ msg = WebSocket::Message.close(1001, 'Goodbye!').to_data
44
+
45
+ messages = parser << msg
46
+ messages.should be_empty
47
+ end
48
+
42
49
  it "can receive a message in parts" do
43
50
  data = WebSocket::Message.new('Once upon a time').to_data
44
51
  parser << data.slice!(0, 5)
@@ -85,26 +92,6 @@ describe WebSocket::Parser do
85
92
  received_messages.first.should == text
86
93
  end
87
94
 
88
- it "can receive multiframe messages" do
89
- frame1 = WebSocket::Message.new("It is a truth universally acknowledged,", :continuation)
90
- frame2 = WebSocket::Message.new("that a single man in possession of a good fortune,", :continuation)
91
- frame3 = WebSocket::Message.new("must be in want of a wife.")
92
-
93
- parser << frame1.to_data
94
-
95
- received_messages.should be_empty
96
-
97
- parser << frame2.to_data
98
-
99
- received_messages.should be_empty
100
-
101
- parser << frame3.to_data
102
-
103
- received_messages.first.should == "It is a truth universally acknowledged," +
104
- "that a single man in possession of a good fortune," +
105
- "must be in want of a wife."
106
- end
107
-
108
95
  it "recognizes a ping message" do
109
96
  parser << WebSocket::Message.ping.to_data
110
97
 
@@ -141,4 +128,58 @@ describe WebSocket::Parser do
141
128
 
142
129
  received_messages.first.should == 'Once upon a time'
143
130
  end
131
+
132
+ context "examples from the spec" do
133
+ # These are literal examples from the spec
134
+
135
+ it "recognizes single-frame unmasked text message" do
136
+ parser << [0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f].pack('C*')
137
+
138
+ received_messages.first.should == 'Hello'
139
+ end
140
+
141
+ it "recognizes single-frame masked text message" do
142
+ parser << [0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58].pack('C*')
143
+
144
+ received_messages.first.should == 'Hello'
145
+ end
146
+
147
+ it "recognizes a fragmented unmasked text message" do
148
+ parser << [0x01, 0x03, 0x48, 0x65, 0x6c].pack('C*') # contains "Hel"
149
+
150
+ received_messages.should be_empty
151
+
152
+ parser << [0x80, 0x02, 0x6c, 0x6f].pack('C*') # contains "lo"
153
+
154
+ received_messages.first.should == 'Hello'
155
+ end
156
+
157
+ it "recognizes an unnmasked ping request" do
158
+ parser << [0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f].pack('C*')
159
+
160
+ received_pings.size.should == 1
161
+ end
162
+
163
+ it "recognizes a masked pong response" do
164
+ parser << [0x8a, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58].pack('C*')
165
+
166
+ received_pongs.size.should == 1
167
+ end
168
+
169
+ it "recognizes 256 bytes binary message in a single unmasked frame" do
170
+ data = Array.new(256) { rand(256) }.pack('c*')
171
+ parser << [0x82, 0x7E, 0x0100].pack('CCS<') + data
172
+
173
+ received_messages.first.should == data
174
+ end
175
+
176
+ it "recoginzes 64KiB binary message in a single unmasked frame" do
177
+ data = Array.new(65536) { rand(256) }.pack('c*')
178
+ parser << [0x82, 0x7F, 0x0000000000010000].pack('CCQ<') + data
179
+
180
+ received_messages.first.should == data
181
+ end
182
+ end
183
+
184
+
144
185
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: websocket_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-09 00:00:00.000000000 Z
12
+ date: 2012-08-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: http
@@ -97,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
97
97
  version: '0'
98
98
  segments:
99
99
  - 0
100
- hash: 2454613231888263104
100
+ hash: -2916391695898748825
101
101
  required_rubygems_version: !ruby/object:Gem::Requirement
102
102
  none: false
103
103
  requirements:
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  version: '0'
107
107
  segments:
108
108
  - 0
109
- hash: 2454613231888263104
109
+ hash: -2916391695898748825
110
110
  requirements: []
111
111
  rubyforge_project:
112
112
  rubygems_version: 1.8.23