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 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
module WebSocket
class Extensions
autoload :Parser, File.expand_path('../extensions/parser', __FILE__)
ExtensionError = Class.new(ArgumentError)
MESSAGE_OPCODES = [1, 2]
def initialize
@rsv1 = @rsv2 = @rsv3 = nil
@by_name = {}
@in_order = []
@sessions = []
@index = {}
end
def add(ext)
unless ext.respond_to?(:name) and ext.name.is_a?(String)
raise TypeError, 'extension.name must be a string'
end
unless ext.respond_to?(:type) and ext.type == 'permessage'
raise TypeError, 'extension.type must be "permessage"'
end
unless ext.respond_to?(:rsv1) and [true, false].include?(ext.rsv1)
raise TypeError, 'extension.rsv1 must be true or false'
end
unless ext.respond_to?(:rsv2) and [true, false].include?(ext.rsv2)
raise TypeError, 'extension.rsv2 must be true or false'
end
unless ext.respond_to?(:rsv3) and [true, false].include?(ext.rsv3)
raise TypeError, 'extension.rsv3 must be true or false'
end
if @by_name.has_key?(ext.name)
raise TypeError, %Q{An extension with name "#{ext.name}" is already registered}
end
@by_name[ext.name] = ext
@in_order.push(ext)
end
def generate_offer
sessions = []
offer = []
index = {}
@in_order.each do |ext|
session = ext.create_client_session
next unless session
record = [ext, session]
sessions.push(record)
index[ext.name] = record
offers = session.generate_offer
offers = offers ? [offers].flatten : []
offers.each do |off|
offer.push(Parser.serialize_params(ext.name, off))
end
end
@sessions = sessions
@index = index
offer.size > 0 ? offer.join(', ') : nil
end
def activate(header)
responses = Parser.parse_header(header)
@sessions = []
responses.each_offer do |name, params|
unless record = @index[name]
raise ExtensionError, %Q{Server sent am extension response for unknown extension "#{name}"}
end
ext, session = *record
if reserved = reserved?(ext)
raise ExtensionError, %Q{Server sent two extension responses that use the RSV#{reserved[0]} } +
%Q{ bit: "#{reserved[1]}" and "#{ext.name}"}
end
unless session.activate(params) == true
raise ExtensionError, %Q{Server send unacceptable extension parameters: #{Parser.serialize_params(name, params)}}
end
reserve(ext)
@sessions.push(record)
end
end
def generate_response(header)
offers = Parser.parse_header(header)
sessions = []
response = []
@in_order.each do |ext|
offer = offers.by_name(ext.name)
next if offer.empty? or reserved?(ext)
next unless session = ext.create_server_session(offer)
reserve(ext)
sessions.push([ext, session])
response.push(Parser.serialize_params(ext.name, session.generate_response))
end
@sessions = sessions
response.size > 0 ? response.join(', ') : nil
end
def valid_frame_rsv(frame)
allowed = {:rsv1 => false, :rsv2 => false, :rsv3 => false}
if MESSAGE_OPCODES.include?(frame.opcode)
@sessions.each do |ext, session|
allowed[:rsv1] ||= ext.rsv1
allowed[:rsv2] ||= ext.rsv2
allowed[:rsv3] ||= ext.rsv3
end
end
(allowed[:rsv1] || !frame.rsv1) &&
(allowed[:rsv2] || !frame.rsv2) &&
(allowed[:rsv3] || !frame.rsv3)
end
alias :valid_frame_rsv? :valid_frame_rsv
def process_incoming_message(message)
@sessions.reverse.inject(message) do |msg, (ext, session)|
begin
session.process_incoming_message(msg)
rescue => error
raise ExtensionError, [ext.name, error.message].join(': ')
end
end
end
def process_outgoing_message(message)
@sessions.inject(message) do |msg, (ext, session)|
begin
session.process_outgoing_message(msg)
rescue => error
raise ExtensionError, [ext.name, error.message].join(': ')
end
end
end
def close
return unless @sessions
@sessions.each do |ext, session|
session.close rescue nil
end
end
private
def reserve(ext)
@rsv1 ||= ext.rsv1 && ext.name
@rsv2 ||= ext.rsv2 && ext.name
@rsv3 ||= ext.rsv3 && ext.name
end
def reserved?(ext)
return [1, @rsv1] if @rsv1 and ext.rsv1
return [2, @rsv2] if @rsv2 and ext.rsv2
return [3, @rsv3] if @rsv3 and ext.rsv3
false
end
end
end
|