Protocol::WebSocket SourceProtocolWebSocketExtensionCompression

module Compression

Provides support for the permessage-deflate extension.

Nested

Definitions

MINIMUM_WINDOW_BITS = 9

Zlib is not capable of handling < 9 window bits.

def self.offer(client_max_window_bits: true, server_max_window_bits: true, client_no_context_takeover: false, server_no_context_takeover: false)

Client offer to server, construct a list of requested compression parameters suitable for the Sec-WebSocket-Extensions header.

Signature

returns Array(String)

a list of compression parameters suitable to send to the server.

Implementation

def self.offer(client_max_window_bits: true, server_max_window_bits: true, client_no_context_takeover: false, server_no_context_takeover: false)
	
	header = [NAME]
	
	case client_max_window_bits
	when 8..15
		header << "client_max_window_bits=#{client_max_window_bits}"
	when true
		header << 'client_max_window_bits'
	else
		raise ArgumentError, "Invalid local maximum window bits!"
	end
	
	if client_no_context_takeover
		header << 'client_no_context_takeover'
	end
	
	case server_max_window_bits
	when 8..15
		header << "server_max_window_bits=#{server_max_window_bits}"
	when true
		# Default (unspecified) to the server maximum window bits.
	else
		raise ArgumentError, "Invalid remote maximum window bits!"
	end
	
	if server_no_context_takeover
		header << 'server_no_context_takeover'
	end
	
	return header
end

def self.negotiate(arguments, **options)

Negotiate on the server a response to client based on the incoming client offer.

Signature

parameter options Hash

a hash of options which are accepted by the server.

returns Array(String)

a list of compression parameters suitable to send back to the client.

Implementation

def self.negotiate(arguments, **options)
	header = [NAME]
	
	arguments.each do |key, value|
		case key
		when "server_no_context_takeover"
			options[:server_no_context_takeover] = true
			header << key
		when "client_no_context_takeover"
			options[:client_no_context_takeover] = true
			header << key
		when "server_max_window_bits"
			value = Integer(value || 15)
			value = MINIMUM_WINDOW_BITS if value < MINIMUM_WINDOW_BITS
			options[:server_max_window_bits] = value
			header << "server_max_window_bits=#{value}"
		when "client_max_window_bits"
			value = Integer(value || 15)
			value = MINIMUM_WINDOW_BITS if value < MINIMUM_WINDOW_BITS
			options[:client_max_window_bits] = value
			header << "client_max_window_bits=#{value}"
		else
			raise ArgumentError, "Unknown option: #{key}!"
		end
	end
	
	# The header which represents the final accepted/negotiated configuration.
	return header, options
end

def self.server(connection, **options)

Signature

parameter options Hash

a hash of options which are accepted by the server.

Implementation

def self.server(connection, **options)
	connection.reserve!(Frame::RSV1)
	
	connection.reader = Inflate.server(connection.reader, **options)
	connection.writer = Deflate.server(connection.writer, **options)
end

def self.accept(arguments, **options)

Accept on the client, the negotiated server response.

Signature

parameter options Hash

a hash of options which are accepted by the client.

parameter arguments Array(String)

a list of compression parameters as accepted/negotiated by the server.

Implementation

def self.accept(arguments, **options)
	arguments.each do |key, value|
		case key
		when "server_no_context_takeover"
			options[:server_no_context_takeover] = true
		when "client_no_context_takeover"
			options[:client_no_context_takeover] = true
		when "server_max_window_bits"
			options[:server_max_window_bits] = Integer(value || 15)
		when "client_max_window_bits"
			options[:client_max_window_bits] = Integer(value || 15)
		else
			raise ArgumentError, "Unknown option: #{key}!"
		end
	end
	
	return options
end

def self.client(connection, **options)

Signature

parameter options Hash

a hash of options which are accepted by the client.

Implementation

def self.client(connection, **options)
	connection.reserve!(Frame::RSV1)
	
	connection.reader = Inflate.client(connection.reader, **options)
	connection.writer = Deflate.client(connection.writer, **options)
end