-
Notifications
You must be signed in to change notification settings - Fork 187
/
framing03.rb
162 lines (133 loc) · 4.55 KB
/
framing03.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# encoding: BINARY
module EventMachine
module WebSocket
module Framing03
def initialize_framing
@data = ''
@application_data_buffer = '' # Used for MORE frames
@frame_type = nil
end
def process_data
error = false
while !error && @data.size > 1
pointer = 0
more = ((@data.getbyte(pointer) & 0b10000000) == 0b10000000) ^ fin
# Ignoring rsv1-3 for now
opcode = @data.getbyte(0) & 0b00001111
pointer += 1
# Ignoring rsv4
length = @data.getbyte(pointer) & 0b01111111
pointer += 1
payload_length = case length
when 127 # Length defined by 8 bytes
# Check buffer size
if @data.getbyte(pointer+8-1) == nil
debug [:buffer_incomplete, @data]
error = true
next
end
# Only using the last 4 bytes for now, till I work out how to
# unpack 8 bytes. I'm sure 4GB frames will do for now :)
l = @data[(pointer+4)..(pointer+7)].unpack('N').first
pointer += 8
l
when 126 # Length defined by 2 bytes
# Check buffer size
if @data.getbyte(pointer+2-1) == nil
debug [:buffer_incomplete, @data]
error = true
next
end
l = @data[pointer..(pointer+1)].unpack('n').first
pointer += 2
l
else
length
end
if payload_length > @connection.max_frame_size
raise WSMessageTooBigError, "Frame length too long (#{payload_length} bytes)"
end
# Check buffer size
if @data.getbyte(pointer+payload_length-1) == nil
debug [:buffer_incomplete, @data]
error = true
next
end
# Throw away data up to pointer
@data.slice!(0...pointer)
# Read application data
application_data = @data.slice!(0...payload_length)
frame_type = opcode_to_type(opcode)
if frame_type == :continuation && !@frame_type
raise WSProtocolError, 'Continuation frame not expected'
end
if more
debug [:moreframe, frame_type, application_data]
@application_data_buffer << application_data
# The message type is passed in the first frame
@frame_type ||= frame_type
else
# Message is complete
if frame_type == :continuation
@application_data_buffer << application_data
message(@frame_type, '', @application_data_buffer)
@application_data_buffer = ''
@frame_type = nil
else
message(frame_type, '', application_data)
end
end
end # end while
end
def send_frame(frame_type, application_data)
debug [:sending_frame, frame_type, application_data]
if @state == :closing && data_frame?(frame_type)
raise WebSocketError, "Cannot send data frame since connection is closing"
end
frame = ''
opcode = type_to_opcode(frame_type)
byte1 = opcode # since more, rsv1-3 are 0
frame << byte1
length = application_data.size
if length <= 125
byte2 = length # since rsv4 is 0
frame << byte2
elsif length < 65536 # write 2 byte length
frame << 126
frame << [length].pack('n')
else # write 8 byte length
frame << 127
frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
end
frame << application_data
@connection.send_data(frame)
end
def send_text_frame(data)
send_frame(:text, data)
end
private
# This allows flipping the more bit to fin for draft 04
def fin; false; end
FRAME_TYPES = {
:continuation => 0,
:close => 1,
:ping => 2,
:pong => 3,
:text => 4,
:binary => 5
}
FRAME_TYPES_INVERSE = FRAME_TYPES.invert
# Frames are either data frames or control frames
DATA_FRAMES = [:text, :binary, :continuation]
def type_to_opcode(frame_type)
FRAME_TYPES[frame_type] || raise("Unknown frame type")
end
def opcode_to_type(opcode)
FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode #{opcode}")
end
def data_frame?(type)
DATA_FRAMES.include?(type)
end
end
end
end