summaryrefslogtreecommitdiff
path: root/spec/unit/http/validate_content_length_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/unit/http/validate_content_length_spec.rb')
-rw-r--r--spec/unit/http/validate_content_length_spec.rb187
1 files changed, 187 insertions, 0 deletions
diff --git a/spec/unit/http/validate_content_length_spec.rb b/spec/unit/http/validate_content_length_spec.rb
new file mode 100644
index 0000000000..091f2b0757
--- /dev/null
+++ b/spec/unit/http/validate_content_length_spec.rb
@@ -0,0 +1,187 @@
+#
+# Author:: Serdar Sutay (<serdar@opscode.com>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'stringio'
+
+describe Chef::HTTP::ValidateContentLength do
+ class TestClient < Chef::HTTP
+ use Chef::HTTP::ValidateContentLength
+ end
+
+ let(:method) { "GET" }
+ let(:url) { "http://dummy.com" }
+ let(:headers) { {} }
+ let(:data) { false }
+
+ let(:request) { }
+ let(:return_value) { "200" }
+
+ # Test Variables
+ let(:request_type) { :streaming }
+ let(:content_length_value) { 23 }
+ let(:streaming_length) { 23 }
+ let(:response_body) { "Thanks for checking in." }
+ let(:response_headers) {
+ {
+ "content-length" => content_length_value
+ }
+ }
+
+ let(:response) {
+ m = double('HttpResponse', :body => response_body)
+ m.stub(:[]) do |key|
+ response_headers[key]
+ end
+
+ m
+ }
+
+ let(:middleware) {
+ client = TestClient.new(url)
+ client.middlewares[0]
+ }
+
+ def run_content_length_validation
+ stream_handler = middleware.stream_response_handler(response)
+ middleware.handle_request(method, url, headers, data)
+
+ case request_type
+ when :streaming
+ # First stream the data
+ data_length = streaming_length
+ while data_length > 0
+ chunk_size = data_length > 10 ? 10 : data_length
+ stream_handler.handle_chunk(double("Chunk", :bytesize => chunk_size))
+ data_length -= chunk_size
+ end
+
+ # Finally call stream complete
+ middleware.handle_stream_complete(response, request, return_value)
+ when :direct
+ middleware.handle_response(response, request, return_value)
+ else
+ raise "Unknown request_type: #{request_type}"
+ end
+ end
+
+ let(:debug_stream) { StringIO.new }
+ let(:debug_output) { debug_stream.string }
+
+ before(:each) {
+ Chef::Log.level = :debug
+ Chef::Log.stub(:debug) do |message|
+ debug_stream.puts message
+ end
+ }
+
+ describe "without response body" do
+ let(:request_type) { :direct }
+ let(:response_body) { "Thanks for checking in." }
+
+ it "shouldn't raise error" do
+ lambda { run_content_length_validation }.should_not raise_error
+ end
+ end
+
+ describe "without Content-Length header" do
+ let(:response_headers) { { } }
+
+ [ "direct", "streaming" ].each do |req_type|
+ describe "when running #{req_type} request" do
+ let(:request_type) { req_type.to_sym }
+
+ it "should skip validation and log for debug" do
+ run_content_length_validation
+ debug_output.should include("HTTP server did not include a Content-Length header in response")
+ end
+ end
+ end
+ end
+
+ describe "with correct Content-Length header" do
+ [ "direct", "streaming" ].each do |req_type|
+ describe "when running #{req_type} request" do
+ let(:request_type) { req_type.to_sym }
+
+ it "should validate correctly" do
+ run_content_length_validation
+ debug_output.should include("Content-Length validated correctly.")
+ end
+ end
+ end
+ end
+
+ describe "with wrong Content-Length header" do
+ let(:content_length_value) { 25 }
+ [ "direct", "streaming" ].each do |req_type|
+ describe "when running #{req_type} request" do
+ let(:request_type) { req_type.to_sym }
+
+ it "should raise ContentLengthMismatch error" do
+ lambda { run_content_length_validation }.should raise_error(Chef::Exceptions::ContentLengthMismatch)
+ end
+ end
+ end
+ end
+
+ describe "when download is interrupted" do
+ let(:streaming_length) { 12 }
+
+ it "should raise ContentLengthMismatch error" do
+ lambda { run_content_length_validation }.should raise_error(Chef::Exceptions::ContentLengthMismatch)
+ end
+ end
+
+ describe "when Transfer-Encoding & Content-Length is set" do
+ let(:response_headers) {
+ {
+ "content-length" => content_length_value,
+ "transfer-encoding" => "chunked"
+ }
+ }
+
+ [ "direct", "streaming" ].each do |req_type|
+ describe "when running #{req_type} request" do
+ let(:request_type) { req_type.to_sym }
+
+ it "should skip validation and log for debug" do
+ run_content_length_validation
+ debug_output.should include("Transfer-Encoding header is set, skipping Content-Length check.")
+ end
+ end
+ end
+ end
+
+ describe "when client is being reused" do
+ before do
+ run_content_length_validation
+ debug_output.should include("Content-Length validated correctly.")
+ end
+
+ it "should reset internal counter" do
+ middleware.instance_variable_get(:@content_length_counter).should be_nil
+ end
+
+ it "should validate correctly second time" do
+ run_content_length_validation
+ debug_output.should include("Content-Length validated correctly.")
+ end
+ end
+
+end