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 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
require 'spec_helper'
require 'puppet/http'
# Simple "external" client to make get & post requests. This is used
# to test the old HTTP API, such as requiring use_ssl and basic_auth
# to be passed as options.
class Puppet::HTTP::TestExternal
def initialize(host, port, options = {})
@host = host
@port = port
@options = options
@factory = Puppet::HTTP::Factory.new
end
def get(path, headers = {}, options = {})
request = Net::HTTP::Get.new(path, headers)
do_request(request, options)
end
def post(path, data, headers = nil, options = {})
request = Net::HTTP::Post.new(path, headers)
do_request(request, options)
end
def do_request(request, options)
if options[:basic_auth]
request.basic_auth(options[:basic_auth][:user], options[:basic_auth][:password])
end
site = Puppet::HTTP::Site.new(@options[:use_ssl] ? 'https' : 'http', @host, @port)
http = @factory.create_connection(site)
http.start
begin
http.request(request)
ensure
http.finish
end
end
end
describe Puppet::HTTP::ExternalClient do
let(:uri) { URI.parse('https://www.example.com') }
let(:http_client_class) { Puppet::HTTP::TestExternal }
let(:client) { described_class.new(http_client_class) }
let(:credentials) { ['user', 'pass'] }
context "for GET requests" do
it "stringifies keys and encodes values in the query" do
stub_request(:get, uri).with(query: "foo=bar%3Dbaz")
client.get(uri, params: {:foo => "bar=baz"})
end
it "fails if a user passes in an invalid param type" do
environment = Puppet::Node::Environment.create(:testing, [])
expect{client.get(uri, params: {environment: environment})}.to raise_error(Puppet::HTTP::SerializationError, /HTTP REST queries cannot handle values of type/)
end
it "returns the response" do
stub_request(:get, uri)
response = client.get(uri)
expect(response).to be_a(Puppet::HTTP::Response)
expect(response).to be_success
expect(response.code).to eq(200)
end
it "returns the entire response body" do
stub_request(:get, uri).to_return(body: "abc")
expect(client.get(uri).body).to eq("abc")
end
it "streams the response body when a block is given" do
stub_request(:get, uri).to_return(body: "abc")
io = StringIO.new
client.get(uri) do |response|
response.read_body do |data|
io.write(data)
end
end
expect(io.string).to eq("abc")
end
context 'when connecting' do
it 'accepts an ssl context' do
stub_request(:get, uri).to_return(body: "abc")
other_context = Puppet::SSL::SSLContext.new
client.get(uri, options: {ssl_context: other_context})
end
it 'accepts include_system_store' do
stub_request(:get, uri).to_return(body: "abc")
client.get(uri, options: {include_system_store: true})
end
end
end
context "for POST requests" do
it "stringifies keys and encodes values in the query" do
stub_request(:post, "https://www.example.com").with(query: "foo=bar%3Dbaz")
client.post(uri, "", params: {:foo => "bar=baz"}, headers: {'Content-Type' => 'text/plain'})
end
it "returns the response" do
stub_request(:post, uri)
response = client.post(uri, "", headers: {'Content-Type' => 'text/plain'})
expect(response).to be_a(Puppet::HTTP::Response)
expect(response).to be_success
expect(response.code).to eq(200)
end
it "sets content-type for the body" do
stub_request(:post, uri).with(headers: {"Content-Type" => "text/plain"})
client.post(uri, "hello", headers: {'Content-Type' => 'text/plain'})
end
it "streams the response body when a block is given" do
stub_request(:post, uri).to_return(body: "abc")
io = StringIO.new
client.post(uri, "", headers: {'Content-Type' => 'text/plain'}) do |response|
response.read_body do |data|
io.write(data)
end
end
expect(io.string).to eq("abc")
end
it 'raises an ArgumentError if `body` is missing' do
expect {
client.post(uri, nil, headers: {'Content-Type' => 'text/plain'})
}.to raise_error(ArgumentError, /'post' requires a string 'body' argument/)
end
context 'when connecting' do
it 'accepts an ssl context' do
stub_request(:post, uri)
other_context = Puppet::SSL::SSLContext.new
client.post(uri, "", headers: {'Content-Type' => 'text/plain'}, options: {ssl_context: other_context})
end
it 'accepts include_system_store' do
stub_request(:post, uri)
client.post(uri, "", headers: {'Content-Type' => 'text/plain'}, options: {include_system_store: true})
end
end
end
context "Basic Auth" do
it "submits credentials for GET requests" do
stub_request(:get, uri).with(basic_auth: credentials)
client.get(uri, options: {basic_auth: {user: 'user', password: 'pass'}})
end
it "submits credentials for POST requests" do
stub_request(:post, uri).with(basic_auth: credentials)
client.post(uri, "", options: {content_type: 'text/plain', basic_auth: {user: 'user', password: 'pass'}})
end
it "returns response containing access denied" do
stub_request(:get, uri).with(basic_auth: credentials).to_return(status: [403, "Ye Shall Not Pass"])
response = client.get(uri, options: { basic_auth: {user: 'user', password: 'pass'}})
expect(response.code).to eq(403)
expect(response.reason).to eq("Ye Shall Not Pass")
expect(response).to_not be_success
end
it 'includes basic auth if user is nil' do
stub_request(:get, uri).with do |req|
expect(req.headers).to include('Authorization')
end
client.get(uri, options: {basic_auth: {user: nil, password: 'pass'}})
end
it 'includes basic auth if password is nil' do
stub_request(:get, uri).with do |req|
expect(req.headers).to include('Authorization')
end
client.get(uri, options: {basic_auth: {user: 'user', password: nil}})
end
end
end
|