Class: Faye::WebSocket::Protocol8Parser

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

Constant Summary

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
}
ERRORS =
{
  :normal_closure => 1000,
  :going_away     => 1001,
  :protocol_error => 1002,
  :unacceptable   => 1003,
  :too_large      => 1004,
  :encoding_error => 1007
}

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Protocol8Parser) initialize(web_socket)

A new instance of Protocol8Parser



48
49
50
51
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 48

def initialize(web_socket)
  reset
  @socket = web_socket
end

Class Method Details

+ (Object) handshake(request)



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 35

def self.handshake(request)
  sec_key = request.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\r\n"
  upgrade
end

Instance Method Details

- (Object) frame(data, type = nil, error_type = nil)



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
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 121

def frame(data, type = nil, error_type = nil)
  return nil if @closed
  
  if error_type
    data = [ERRORS[error_type]].pack('n') + data
  end
  
  opcode = OPCODES[type || :text]
  frame  = (FIN | opcode).chr
  length = data.size
  
  case length
    when 0..125 then
      frame << length.chr
    when 126..65535 then
      frame << 126.chr
      frame << [length].pack('n')
    else
      frame << 127.chr
      frame << [length >> 32, length & 0xFFFFFFFF].pack('NN')
  end
  
  @socket.encode(frame, 'UTF-8') +
  @socket.encode(data, 'UTF-8')
end

- (Object) parse(data)



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
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 57

def parse(data)
  byte0  = getbyte(data, 0)
  final  = (byte0 & FIN) == FIN
  opcode = (byte0 & OPCODE)
  
  return close(:protocol_error) unless OPCODES.values.include?(opcode)
  reset unless opcode == OPCODES[:continuation]

  byte1  = getbyte(data, 1)
  masked = (byte1 & MASK) == MASK
  length = (byte1 & LENGTH)
  offset = 0
  
  case length
    when 126 then
      length = integer(data, 2, 2)
      offset = 2
    when 127 then
      length = integer(data, 2, 8)
      offset = 8
  end
  
  if masked
    payload_offset = 2 + offset + 4 
    mask_octets    = (0..3).map { |i| getbyte(data, 2 + offset + i) }
  else
    payload_offset = 2 + offset
    mask_octets    = []
  end
  
  return close(:too_large) if getbyte(data, payload_offset + length)
  
  raw_payload = data[payload_offset...(payload_offset + length)]
  payload     = unmask(raw_payload, mask_octets)

  case opcode
    when OPCODES[:continuation] then
      return unless @mode == :text
      @buffer << payload
      if final
        message = @buffer * ''
        reset
        @socket.receive(message)
      end

    when OPCODES[:text] then
      if final
        @socket.receive(payload)
      else
        @mode = :text
        @buffer << payload
      end

    when OPCODES[:binary] then
      close(:unacceptable)

    when OPCODES[:close] then
      close(:normal_closure)

    when OPCODES[:ping] then
      @socket.send(payload, :pong)
  end
end

- (Object) version



53
54
55
# File 'lib/faye/util/web_socket/protocol8_parser.rb', line 53

def version
  'protocol-8'
end