Unit tests for the ClientSession compression options and offer negotiation.
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
language: ruby
|
||||
|
||||
rvm:
|
||||
- 1.9.3
|
||||
- 2.0.0
|
||||
- 2.1.3
|
||||
- jruby-18mode
|
||||
- jruby-19mode
|
||||
- rbx-2.2
|
||||
|
||||
script: bundle exec rspec -c spec/
|
||||
@@ -6,6 +6,8 @@ class PermessageDeflate
|
||||
require root + '/permessage_deflate/client_session'
|
||||
require root + '/permessage_deflate/server_session'
|
||||
|
||||
ConfigurationError = Class.new(ArgumentError)
|
||||
|
||||
module Extension
|
||||
define_method(:name) { 'permessage-deflate' }
|
||||
define_method(:type) { 'permessage' }
|
||||
@@ -13,16 +15,26 @@ class PermessageDeflate
|
||||
define_method(:rsv2) { false }
|
||||
define_method(:rsv3) { false }
|
||||
|
||||
def configure(options)
|
||||
options = (@options || {}).merge(options)
|
||||
PermessageDeflate.new(options)
|
||||
end
|
||||
|
||||
def create_client_session
|
||||
ClientSession.new
|
||||
ClientSession.new(@options || {})
|
||||
end
|
||||
|
||||
def create_server_session(offers)
|
||||
offers.each do |offer|
|
||||
return ServerSession.new(offer) if ServerSession.valid_params?(offer)
|
||||
return ServerSession.new(@options || {}, offer) if ServerSession.valid_params?(offer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
extend Extension
|
||||
include Extension
|
||||
extend Extension
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,14 +12,48 @@ class PermessageDeflate
|
||||
end
|
||||
|
||||
def generate_offer
|
||||
[{'client_max_window_bits' => true}]
|
||||
offer = {}
|
||||
|
||||
if @accept_no_context_takeover
|
||||
offer['client_no_context_takeover'] = true
|
||||
end
|
||||
|
||||
if @accept_max_window_bits
|
||||
raise ConfigurationError unless VALID_WINDOW_BITS.include?(@accept_max_window_bits)
|
||||
offer['client_max_window_bits'] = @accept_max_window_bits
|
||||
else
|
||||
offer['client_max_window_bits'] = true
|
||||
end
|
||||
|
||||
if @request_no_context_takeover
|
||||
offer['server_no_context_takeover'] = true
|
||||
end
|
||||
|
||||
if @request_max_window_bits
|
||||
raise ConfigurationError unless VALID_WINDOW_BITS.include?(@request_max_window_bits)
|
||||
offer['server_max_window_bits'] = @request_max_window_bits
|
||||
end
|
||||
|
||||
offer
|
||||
end
|
||||
|
||||
def activate(params)
|
||||
return false unless ClientSession.valid_params?(params)
|
||||
|
||||
@own_context_takeover = !params['client_no_context_takeover']
|
||||
@own_window_bits = params['client_max_window_bits'] || DEFAULT_MAX_WINDOW_BITS
|
||||
if @accept_max_window_bits and params['client_max_window_bits']
|
||||
return false if params['client_max_window_bits'] > @accept_max_window_bits
|
||||
end
|
||||
|
||||
if @request_no_context_takeover and !params['server_no_context_takeover']
|
||||
return false
|
||||
end
|
||||
|
||||
if @request_max_window_bits
|
||||
return false unless params['server_max_window_bits'] and params['server_max_window_bits'] <= @request_max_window_bits
|
||||
end
|
||||
|
||||
@own_context_takeover = !(@accept_no_context_takeover || params['client_no_context_takeover'])
|
||||
@own_window_bits = [@accept_max_window_bits, params['client_max_window_bits']].map { |x| x || DEFAULT_MAX_WINDOW_BITS }.min
|
||||
|
||||
@peer_context_takeover = !params['server_no_context_takeover']
|
||||
@peer_window_bits = params['server_max_window_bits'] || DEFAULT_MAX_WINDOW_BITS
|
||||
|
||||
@@ -11,8 +11,8 @@ class PermessageDeflate
|
||||
true
|
||||
end
|
||||
|
||||
def initialize(params)
|
||||
super()
|
||||
def initialize(options, params)
|
||||
super(options)
|
||||
@params = params
|
||||
end
|
||||
|
||||
|
||||
@@ -31,6 +31,17 @@ class PermessageDeflate
|
||||
true
|
||||
end
|
||||
|
||||
def initialize(options)
|
||||
@level = options[:level] || Zlib::DEFAULT_COMPRESSION
|
||||
@mem_level = options[:mem_level] || Zlib::DEF_MEM_LEVEL
|
||||
@strategy = options[:strategy] || Zlib::DEFAULT_STRATEGY
|
||||
|
||||
@accept_no_context_takeover = options[:no_context_takeover]
|
||||
@accept_max_window_bits = options[:max_window_bits]
|
||||
@request_no_context_takeover = options[:request_no_context_takeover]
|
||||
@request_max_window_bits = options[:request_max_window_bits]
|
||||
end
|
||||
|
||||
def valid_frame_rsv(frame)
|
||||
if MESSAGE_OPCODES.include?(frame.opcode)
|
||||
{:rsv1 => true, :rsv2 => false, :rsv3 => false}
|
||||
@@ -86,7 +97,7 @@ class PermessageDeflate
|
||||
|
||||
def get_deflate
|
||||
return @deflate if @deflate
|
||||
deflate = Zlib::Deflate.new(Zlib::DEFAULT_COMPRESSION, -@own_window_bits)
|
||||
deflate = Zlib::Deflate.new(@level, -@own_window_bits, @mem_level, @strategy)
|
||||
@deflate = deflate if @own_context_takeover
|
||||
deflate
|
||||
end
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
require "spec_helper"
|
||||
|
||||
describe PermessageDeflate::ClientSession do
|
||||
let(:ext) { PermessageDeflate.configure(options) }
|
||||
let(:session) { ext.create_client_session }
|
||||
let(:options) { {} }
|
||||
let(:offer) { session.generate_offer }
|
||||
let(:response) { {} }
|
||||
let(:activate) { session.activate(response) }
|
||||
|
||||
let(:deflate) { double(:deflate, :deflate => "") }
|
||||
let(:inflate) { double(:inflate, :inflate => "") }
|
||||
let(:level) { Zlib::DEFAULT_COMPRESSION }
|
||||
let(:mem_level) { Zlib::DEF_MEM_LEVEL }
|
||||
let(:strategy) { Zlib::DEFAULT_STRATEGY }
|
||||
|
||||
let(:message) { Message.new("hello", true) }
|
||||
|
||||
def process_incoming_message
|
||||
session.process_incoming_message(message)
|
||||
end
|
||||
|
||||
def process_outgoing_message
|
||||
session.process_outgoing_message(message)
|
||||
end
|
||||
|
||||
describe "with default options" do
|
||||
it "indicates support for client_max_window_bits" do
|
||||
expect(offer).to eq("client_max_window_bits" => true)
|
||||
end
|
||||
|
||||
describe "with an empty response" do
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses context takeover and 15 window bits for inflating incoming messages" do
|
||||
activate
|
||||
expect(Zlib::Inflate).to receive(:new).with(-15).exactly(1).and_return(inflate)
|
||||
process_incoming_message
|
||||
process_incoming_message
|
||||
end
|
||||
|
||||
it "uses context takeover and 15 window bits for deflating outgoing messages" do
|
||||
activate
|
||||
expect(Zlib::Deflate).to receive(:new).with(level, -15, mem_level, strategy).exactly(1).and_return(deflate)
|
||||
process_outgoing_message
|
||||
process_outgoing_message
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response includes server_no_context_takeover" do
|
||||
before { response["server_no_context_takeover"] = true }
|
||||
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses no context takeover and 15 window bits for inflating incoming messages" do
|
||||
activate
|
||||
expect(Zlib::Inflate).to receive(:new).with(-15).exactly(2).and_return(inflate)
|
||||
expect(inflate).to receive(:finish).exactly(2)
|
||||
expect(inflate).to receive(:close).exactly(2)
|
||||
process_incoming_message
|
||||
process_incoming_message
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response includes client_no_context_takeover" do
|
||||
before { response["client_no_context_takeover"] = true }
|
||||
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses no context takeover and 15 window bits for deflating outgoing messages" do
|
||||
activate
|
||||
expect(Zlib::Deflate).to receive(:new).with(level, -15, mem_level, strategy).exactly(2).and_return(deflate)
|
||||
expect(deflate).to receive(:finish).exactly(2)
|
||||
expect(deflate).to receive(:close).exactly(2)
|
||||
process_outgoing_message
|
||||
process_outgoing_message
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response includes server_max_window_bits" do
|
||||
before { response["server_max_window_bits"] = 8 }
|
||||
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses context takeover and 8 window bits for inflating incoming messages" do
|
||||
activate
|
||||
expect(Zlib::Inflate).to receive(:new).with(-8).exactly(1).and_return(inflate)
|
||||
process_incoming_message
|
||||
process_incoming_message
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response includes invalid server_max_window_bits" do
|
||||
before { response["server_max_window_bits"] = 20 }
|
||||
|
||||
it "rejects the response" do
|
||||
expect(activate).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response includes client_max_window_bits" do
|
||||
before { response["client_max_window_bits"] = 8 }
|
||||
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses context takeover and 8 window bits for deflating outgoing messages" do
|
||||
activate
|
||||
expect(Zlib::Deflate).to receive(:new).with(level, -8, mem_level, strategy).exactly(1).and_return(deflate)
|
||||
process_outgoing_message
|
||||
process_outgoing_message
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response includes invalid client_max_window_bits" do
|
||||
before { response["client_max_window_bits"] = 20 }
|
||||
|
||||
it "rejects the response" do
|
||||
expect(activate).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with no_context_takeover" do
|
||||
before { options[:no_context_takeover] = true }
|
||||
|
||||
it "sends client_no_context_takeover with no_context_takeover" do
|
||||
expect(offer).to eq(
|
||||
"client_no_context_takeover" => true,
|
||||
"client_max_window_bits" => true
|
||||
)
|
||||
end
|
||||
|
||||
describe "with an empty response" do
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses no context takeover and 15 window bits for deflating outgoing messages" do
|
||||
activate
|
||||
expect(Zlib::Deflate).to receive(:new).with(level, -15, mem_level, strategy).exactly(2).and_return(deflate)
|
||||
expect(deflate).to receive(:finish).exactly(2)
|
||||
expect(deflate).to receive(:close).exactly(2)
|
||||
process_outgoing_message
|
||||
process_outgoing_message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with max_window_bits" do
|
||||
before { options[:max_window_bits] = 9 }
|
||||
|
||||
it "sends client_max_window_bits with max_window_bits" do
|
||||
expect(offer).to eq("client_max_window_bits" => 9)
|
||||
end
|
||||
|
||||
describe "with an empty response" do
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses context takeover and 9 window bits for deflating outgoing messages" do
|
||||
activate
|
||||
expect(Zlib::Deflate).to receive(:new).with(level, -9, mem_level, strategy).exactly(1).and_return(deflate)
|
||||
process_outgoing_message
|
||||
process_outgoing_message
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response has higher client_max_window_bits" do
|
||||
before { response["client_max_window_bits"] = 10 }
|
||||
|
||||
it "does not accept the response" do
|
||||
expect(activate).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response has lower client_max_window_bits" do
|
||||
before { response["client_max_window_bits"] = 8 }
|
||||
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses context takeover and 8 window bits for deflating outgoing messages" do
|
||||
activate
|
||||
expect(Zlib::Deflate).to receive(:new).with(level, -8, mem_level, strategy).exactly(1).and_return(deflate)
|
||||
process_outgoing_message
|
||||
process_outgoing_message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with invalid max_window_bits" do
|
||||
before { options[:max_window_bits] = 20 }
|
||||
|
||||
it "raises when generating the offer with invalid max_window_bits" do
|
||||
expect { offer }.to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "with request_no_context_takeover" do
|
||||
before { options[:request_no_context_takeover] = true }
|
||||
|
||||
it "sends server_no_context_takeover with request_no_context_takeover" do
|
||||
expect(offer).to eq(
|
||||
"client_max_window_bits" => true,
|
||||
"server_no_context_takeover" => true
|
||||
)
|
||||
end
|
||||
|
||||
describe "with an empty response" do
|
||||
it "rejects the response" do
|
||||
expect(activate).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response includes server_no_context_takeover" do
|
||||
before { response["server_no_context_takeover"] = true }
|
||||
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses no no context takeover and 15 window bits for inflating incoming messages" do
|
||||
activate
|
||||
expect(Zlib::Inflate).to receive(:new).with(-15).exactly(2).and_return(inflate)
|
||||
expect(inflate).to receive(:finish).exactly(2)
|
||||
expect(inflate).to receive(:close).exactly(2)
|
||||
process_incoming_message
|
||||
process_incoming_message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with request_max_window_bits" do
|
||||
before { options[:request_max_window_bits] = 12 }
|
||||
|
||||
it "sends server_max_window_bits with request_max_window_bits" do
|
||||
expect(offer).to eq(
|
||||
"client_max_window_bits" => true,
|
||||
"server_max_window_bits" => 12
|
||||
)
|
||||
end
|
||||
|
||||
describe "with an empty response" do
|
||||
it "rejects the response" do
|
||||
expect(activate).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response has higher server_max_window_bits" do
|
||||
before { response["server_max_window_bits"] = 13 }
|
||||
|
||||
it "rejects the response" do
|
||||
expect(activate).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the response has lower server_max_window_bits" do
|
||||
before { response["server_max_window_bits"] = 11 }
|
||||
|
||||
it "accepts the response" do
|
||||
expect(activate).to be true
|
||||
end
|
||||
|
||||
it "uses context takeover and 11 window bits for inflating incoming messages" do
|
||||
activate
|
||||
expect(Zlib::Inflate).to receive(:new).with(-11).exactly(1).and_return(inflate)
|
||||
process_incoming_message
|
||||
process_incoming_message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with invalid request_max_window_bits" do
|
||||
before { options[:request_max_window_bits] = 20 }
|
||||
|
||||
it "raises when generating an offer with invalid request_max_window_bits" do
|
||||
expect { offer }.to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "with level" do
|
||||
before { options[:level] = Zlib::BEST_SPEED }
|
||||
|
||||
it "sets the level of the deflate stream" do
|
||||
activate
|
||||
expect(Zlib::Deflate).to receive(:new).with(Zlib::BEST_SPEED, -15, mem_level, strategy).and_return(deflate)
|
||||
process_outgoing_message
|
||||
end
|
||||
end
|
||||
|
||||
describe "with mem_level" do
|
||||
before { options[:mem_level] = 5 }
|
||||
|
||||
it "sets the mem_level of the deflate stream" do
|
||||
activate
|
||||
expect(Zlib::Deflate).to receive(:new).with(level, -15, 5, strategy).and_return(deflate)
|
||||
process_outgoing_message
|
||||
end
|
||||
end
|
||||
|
||||
describe "with strategy" do
|
||||
before { options[:strategy] = Zlib::RLE }
|
||||
|
||||
it "sets the strategy of the deflate stream" do
|
||||
activate
|
||||
expect(Zlib::Deflate).to receive(:new).with(level, -15, mem_level, Zlib::RLE).and_return(deflate)
|
||||
process_outgoing_message
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
require File.expand_path("../../lib/permessage_deflate", __FILE__)
|
||||
|
||||
class Message < Struct.new(:data, :rsv1)
|
||||
end
|
||||
Reference in New Issue
Block a user