Module: EventMachine::WebSocket::Framing03

Included in:
Framing04, Handler03
Defined in:
lib/em-websocket/framing03.rb

Constant Summary

FRAME_TYPES =
{
  :continuation => 0,
  :close => 1,
  :ping => 2,
  :pong => 3,
  :text => 4,
  :binary => 5
}
FRAME_TYPES_INVERSE =
FRAME_TYPES.invert
DATA_FRAMES =

Frames are either data frames or control frames

[:text, :binary, :continuation]

Instance Method Summary (collapse)

Instance Method Details

- (Object) initialize_framing



6
7
8
9
# File 'lib/em-websocket/framing03.rb', line 6

def initialize_framing
  @data = ''
  @application_data_buffer = '' # Used for MORE frames
end

- (Object) process_data(newdata)



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
# File 'lib/em-websocket/framing03.rb', line 11

def process_data(newdata)
  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

- (Object) send_frame(frame_type, application_data)



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
# File 'lib/em-websocket/framing03.rb', line 97

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

- (Object) send_text_frame(data)



127
128
129
# File 'lib/em-websocket/framing03.rb', line 127

def send_text_frame(data)
  send_frame(:text, data)
end