Module: EventMachine::WebSocket::Framing76

Included in:
Handler75, Handler76
Defined in:
lib/em-websocket/framing76.rb

Instance Method Summary collapse

Instance Method Details

#initialize_framingObject


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

def initialize_framing
  @data = ''
end

#process_data(newdata) ⇒ Object


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

def process_data(newdata)
  debug [:message, @data]

  # This algorithm comes straight from the spec
  # http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-5.3

  error = false

  while !error
    return if @data.size == 0

    pointer = 0
    frame_type = @data.getbyte(pointer)
    pointer += 1

    if (frame_type & 0x80) == 0x80
      # If the high-order bit of the /frame type/ byte is set
      length = 0

      loop do
        return false if !@data.getbyte(pointer)
        b = @data.getbyte(pointer)
        pointer += 1
        b_v = b & 0x7F
        length = length * 128 + b_v
        break unless (b & 0x80) == 0x80
      end

      if length > @connection.max_frame_size
        raise WSMessageTooBigError, "Frame length too long (#{length} bytes)"
      end

      if @data.getbyte(pointer+length-1) == nil
        debug [:buffer_incomplete, @data]
        # Incomplete data - leave @data to accumulate
        error = true
      else
        # Straight from spec - I'm sure this isn't crazy...
        # 6. Read /length/ bytes.
        # 7. Discard the read bytes.
        @data = @data[(pointer+length)..-1]

        # If the /frame type/ is 0xFF and the /length/ was 0, then close
        if length == 0
          @connection.send_data("\xff\x00")
          @state = :closing
          @connection.close_connection_after_writing
        else
          error = true
        end
      end
    else
      # If the high-order bit of the /frame type/ byte is _not_ set

      if @data.getbyte(0) != 0x00
        # Close the connection since this buffer can never match
        raise WSProtocolError, "Invalid frame received"
      end

      # Addition to the spec to protect against malicious requests
      if @data.size > @connection.max_frame_size
        raise WSMessageTooBigError, "Frame length too long (#{@data.size} bytes)"
      end

      # Optimization to avoid calling slice! unnecessarily
      error = true and next unless newdata =~ /\xff/

      msg = @data.slice!(/\A\x00[^\xff]*\xff/)
      if msg
        msg.gsub!(/\A\x00|\xff\z/, '')
        if @state == :closing
          debug [:ignored_message, msg]
        else
          msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
          @connection.trigger_on_message(msg)
        end
      else
        error = true
      end
    end
  end

  false
end

#send_text_frame(data) ⇒ Object

frames need to start with 0x00-0x7f byte and end with an 0xFF byte. Per spec, we can also set the first byte to a value betweent 0x80 and 0xFF, followed by a leading length indicator


99
100
101
102
103
104
# File 'lib/em-websocket/framing76.rb', line 99

def send_text_frame(data)
  debug [:sending_text_frame, data]
  ary = ["\x00", data, "\xff"]
  ary.collect{ |s| s.force_encoding('UTF-8') if s.respond_to?(:force_encoding) }
  @connection.send_data(ary.join)
end