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
|
require 'strscan'
module WebSocket
class Extensions
class Parser
TOKEN = /([!#\$%&'\*\+\-\.\^_`\|~0-9a-z]+)/
NOTOKEN = /([^!#\$%&'\*\+\-\.\^_`\|~0-9a-z])/
QUOTED = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"/
PARAM = %r{#{TOKEN.source}(?:=(?:#{TOKEN.source}|#{QUOTED.source}))?}
EXT = %r{#{TOKEN.source}(?: *; *#{PARAM.source})*}
EXT_LIST = %r{^#{EXT.source}(?: *, *#{EXT.source})*$}
NUMBER = /^-?(0|[1-9][0-9]*)(\.[0-9]+)?$/
ParseError = Class.new(ArgumentError)
def self.parse_header(header)
offers = Offers.new
return offers if header == '' or header.nil?
unless header =~ EXT_LIST
raise ParseError, "Invalid Sec-WebSocket-Extensions header: #{header}"
end
scanner = StringScanner.new(header)
value = scanner.scan(EXT)
until value.nil?
params = value.scan(PARAM)
name = params.shift.first
offer = {}
params.each do |key, unquoted, quoted|
if unquoted
data = unquoted
elsif quoted
data = quoted.gsub(/\\/, '')
else
data = true
end
if data =~ NUMBER
data = data =~ /\./ ? data.to_f : data.to_i(10)
end
if offer.has_key?(key)
offer[key] = [*offer[key]] + [data]
else
offer[key] = data
end
end
offers.push(name, offer)
scanner.scan(/ *, */)
value = scanner.scan(EXT)
end
offers
end
def self.serialize_params(name, params)
values = []
print = lambda do |key, value|
case value
when Array then value.each { |v| print[key, v] }
when true then values.push(key)
when Numeric then values.push(key + '=' + value.to_s)
else
if value =~ NOTOKEN
values.push(key + '="' + value.gsub(/"/, '\"') + '"')
else
values.push(key + '=' + value)
end
end
end
params.keys.sort.each { |key| print[key, params[key]] }
([name] + values).join('; ')
end
end
class Offers
def initialize
@by_name = {}
@in_order = []
end
def push(name, params)
@by_name[name] ||= []
@by_name[name].push(params)
@in_order.push(:name => name, :params => params)
end
def each_offer(&block)
@in_order.each do |offer|
block.call(offer[:name], offer[:params])
end
end
def by_name(name)
@by_name[name] || []
end
def to_a
@in_order.dup
end
end
end
end
|