Don't catch errors from event listeners. Instead, make sure that in all parsing methods in Hybi, @stage is set before any user called is invoked so the parser is in the right state before code that might raise is called.

This commit is contained in:
James Coglan
2015-07-05 18:45:07 +01:00
parent f11d5905d2
commit fa11841bfa
3 changed files with 18 additions and 46 deletions
-10
View File
@@ -121,17 +121,7 @@ module WebSocket
private
def emit(*args)
super
rescue Exception => error
emit(:error, error)
shutdown(1011, error.message)
end
def shutdown(code, reason)
@ready_state = 3
@stage = 5
emit(:close, CloseEvent.new(code, reason))
end
def open
+18 -17
View File
@@ -108,15 +108,15 @@ module WebSocket
when 3 then
buffer = @reader.read(4)
if buffer
@frame.masking_key = buffer
@stage = 4
@frame.masking_key = buffer
end
when 4 then
buffer = @reader.read(@frame.length)
if buffer
emit_frame(buffer)
@stage = 0
emit_frame(buffer)
end
else
@@ -255,17 +255,19 @@ module WebSocket
headers.join("\r\n")
end
def shutdown(code, reason)
def shutdown(code, reason, &error)
frame(reason, :close, code) if @ready_state < 2
@frame = @message = nil
@ready_state = 3
@stage = 5
@extensions.close
super
error.call if error
emit(:close, CloseEvent.new(code, reason))
end
def fail(type, message)
return if @ready_state > 1
emit(:error, ProtocolError.new(message))
shutdown(ERRORS[type], message)
shutdown(ERRORS[type], message) { emit(:error, ProtocolError.new(message)) }
end
def parse_opcode(data)
@@ -279,6 +281,8 @@ module WebSocket
@frame.rsv3 = rsvs[2]
@frame.opcode = (data & OPCODE)
@stage = 1
unless @extensions.valid_frame_rsv?(@frame)
return fail(:protocol_error,
"One or more reserved bits are on: reserved1 = #{@frame.rsv1 ? 1 : 0}" +
@@ -297,37 +301,34 @@ module WebSocket
if @message and OPENING_OPCODES.include?(@frame.opcode)
return fail(:protocol_error, 'Received new data frame but previous continuous frame is unfinished')
end
@stage = 1
end
def parse_length(data)
@frame.masked = (data & MASK) == MASK
if @require_masking and not @frame.masked
return fail(:unacceptable, 'Received unmasked frame but masking is required')
end
@frame.length = (data & LENGTH)
@frame.masked = (data & MASK) == MASK
if @frame.length >= 0 and @frame.length <= 125
return unless check_frame_length
@stage = @frame.masked ? 3 : 4
return unless check_frame_length
else
@frame.length_bytes = (@frame.length == 126) ? 2 : 8
@stage = 2
@frame.length_bytes = (@frame.length == 126) ? 2 : 8
end
if @require_masking and not @frame.masked
return fail(:unacceptable, 'Received unmasked frame but masking is required')
end
end
def parse_extended_length(buffer)
@frame.length = integer(buffer)
@stage = @frame.masked ? 3 : 4
unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.length <= 125
return fail(:protocol_error, "Received control frame having too long payload: #{@frame.length}")
end
return unless check_frame_length
@stage = @frame.masked ? 3 : 4
end
def check_frame_length
-19
View File
@@ -358,25 +358,6 @@ describe WebSocket::Driver::Hybi do
driver.parse [0x89, 0x04, 0x4f, 0x48, 0x41, 0x49].pack("C*")
expect(@bytes).to eq [0x8a, 0x04, 0x4f, 0x48, 0x41, 0x49]
end
describe "if a message listener raises an error" do
before do
driver.on :message, -> e { raise StandardError, "event error" }
driver.parse [0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f].pack("C*")
end
it "triggers the onerror event" do
expect(@error.message).to eq("event error")
end
it "triggers the onclose event" do
expect(@close).to eq [1011, "event error"]
end
it "changes the state to :closed" do
expect(driver.state).to eq :closed
end
end
end
describe :frame do