Class: Faye::WebSocket::Protocol8Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/faye/util/web_socket/protocol8_parser.rb

Defined Under Namespace

Classes: Handshake

Constant Summary collapse

GUID =
'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
FIN =
MASK = 0b10000000
RSV1 =
0b01000000
RSV2 =
0b00100000
RSV3 =
0b00010000
OPCODE =
0b00001111
LENGTH =
0b01111111
OPCODES =
{
  :continuation => 0,
  :text         => 1,
  :binary       => 2,
  :close        => 8,
  :ping         => 9,
  :pong         => 10
}
FRAGMENTED_OPCODES =
OPCODES.values_at(:continuation, :text, :binary)
OPENING_OPCODES =
OPCODES.values_at(:text, :binary)
ERRORS =
{
  :normal_closure   => 1000,
  :going_away       => 1001,
  :protocol_error   => 1002,
  :unacceptable     => 1003,
  :encoding_error   => 1007,
  :policy_violation => 1008,
  :too_large        => 1009,
  :extension_error  => 1010
}
ERROR_CODES =
ERRORS.values

Instance Method Summary collapse

Constructor Details

#initialize(web_socket, options = {}) ⇒ Protocol8Parser

Returns a new instance of Protocol8Parser.



92
93
94
95
96
97
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 92

def initialize(web_socket, options = {})
  reset
  @socket  = web_socket
  @stage   = 0
  @masking = options[:masking]
end

Instance Method Details

#close(code = nil, reason = nil, &callback) ⇒ Object



170
171
172
173
174
175
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 170

def close(code = nil, reason = nil, &callback)
  return if @closed
  @closing_callback ||= callback
  @socket.send(reason || '', :close, code || ERRORS[:normal_closure])
  @closed = true
end

#create_handshakeObject



117
118
119
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 117

def create_handshake
  Handshake.new(@socket.uri)
end

#frame(data, type = nil, code = nil) ⇒ Object



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
163
164
165
166
167
168
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 134

def frame(data, type = nil, code = nil)
  return nil if @closed
  
  type ||= (String === data ? :text : :binary)
  data   = data.bytes.to_a if data.respond_to?(:bytes)
  
  if code
    data = [code].pack('n').bytes.to_a + data
  end
  
  frame  = (FIN | OPCODES[type]).chr
  length = data.size
  masked = @masking ? MASK : 0
  
  case length
    when 0..125 then
      frame << (masked | length).chr
    when 126..65535 then
      frame << (masked | 126).chr
      frame << [length].pack('n')
    else
      frame << (masked | 127).chr
      frame << [length >> 32, length & 0xFFFFFFFF].pack('NN')
  end
  
  if @masking
    mask = (1..4).map { rand 256 }
    data.each_with_index do |byte, i|
      data[i] = byte ^ mask[i % 4]
    end
    frame << mask.pack('C*')
  end
  
  Faye.encode(frame) + Faye.encode(data)
end

#handshake_responseObject



103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 103

def handshake_response
  sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
  return '' unless String === sec_key
  
  accept = Base64.encode64(Digest::SHA1.digest(sec_key + GUID)).strip
  
  upgrade =  "HTTP/1.1 101 Switching Protocols\r\n"
  upgrade << "Upgrade: websocket\r\n"
  upgrade << "Connection: Upgrade\r\n"
  upgrade << "Sec-WebSocket-Accept: #{accept}\r\n"
  upgrade << "\r\n"
  upgrade
end

#parse(data) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 121

def parse(data)
  data.each_byte do |byte|
    case @stage
    when 0 then parse_opcode(byte)
    when 1 then parse_length(byte)
    when 2 then parse_extended_length(byte)
    when 3 then parse_mask(byte)
    when 4 then parse_payload(byte)
    end
    emit_frame if @stage == 4 and @length == 0
  end
end

#versionObject



99
100
101
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 99

def version
  'protocol-8'
end