websocket_parser 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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