class Puppet::Network::HTTP::ConnectionAdapter < Puppet::Network::HTTP::Connection
def initialize(host, port, options = {})
super(host, port, options)
@client = Puppet.runtime[:http]
end
def get(path, headers = {}, options = {})
headers ||= {}
options[:ssl_context] ||= resolve_ssl_context
options[:redirect_limit] ||= @redirect_limit
with_error_handling do
resp = @client.get(to_url(path), headers: headers, options: options)
resp.nethttp
end
end
def post(path, data, headers = nil, options = {})
headers ||= {}
headers['Content-Type'] ||= "application/x-www-form-urlencoded"
data ||= ''
options[:ssl_context] ||= resolve_ssl_context
options[:redirect_limit] ||= @redirect_limit
with_error_handling do
resp = @client.post(to_url(path), data, headers: headers, options: options)
resp.nethttp
end
end
def head(path, headers = {}, options = {})
headers ||= {}
options[:ssl_context] ||= resolve_ssl_context
options[:redirect_limit] ||= @redirect_limit
with_error_handling do
resp = @client.head(to_url(path), headers: headers, options: options)
resp.nethttp
end
end
def delete(path, headers = {'Depth' => 'Infinity'}, options = {})
headers ||= {}
options[:ssl_context] ||= resolve_ssl_context
options[:redirect_limit] ||= @redirect_limit
with_error_handling do
resp = @client.delete(to_url(path), headers: headers, options: options)
resp.nethttp
end
end
def put(path, data, headers = nil, options = {})
headers ||= {}
headers['Content-Type'] ||= "application/x-www-form-urlencoded"
data ||= ''
options[:ssl_context] ||= resolve_ssl_context
options[:redirect_limit] ||= @redirect_limit
with_error_handling do
resp = @client.put(to_url(path), data, headers: headers, options: options)
resp.nethttp
end
end
def request_get(*args, &block)
path, headers = *args
headers ||= {}
options = {
ssl_context: resolve_ssl_context,
redirect_limit: @redirect_limit
}
resp = @client.get(to_url(path), headers: headers, options: options) do |response|
yield response.nethttp if block_given?
end
resp.nethttp
end
def request_head(*args, &block)
path, headers = *args
headers ||= {}
options = {
ssl_context: resolve_ssl_context,
redirect_limit: @redirect_limit
}
response = @client.head(to_url(path), headers: headers, options: options)
yield response.nethttp if block_given?
response.nethttp
end
def request_post(*args, &block)
path, data, headers = *args
headers ||= {}
headers['Content-Type'] ||= "application/x-www-form-urlencoded"
options = {
ssl_context: resolve_ssl_context,
redirect_limit: @redirect_limit
}
resp = @client.post(to_url(path), data, headers: headers, options: options) do |response|
yield response.nethttp if block_given?
end
resp.nethttp
end
private
# The old Connection class ignores the ssl_context on the Puppet stack,
# and always loads certs/keys based on what is currently in the filesystem.
# If the files are missing, it would attempt to bootstrap the certs/keys
# while in the process of making a network request, due to the call to
# Puppet.lookup(:ssl_host) in Puppet::SSL::Validator::DefaultValidator#setup_connection.
# This class doesn't preserve the boostrap behavior because that is handled
# outside of this class, and can only be triggered by running `puppet ssl` or
# `puppet agent`.
def resolve_ssl_context
# don't need an ssl context for http connections
return nil unless @site.use_ssl?
# if our verifier has an ssl_context, use that
ctx = @verifier.ssl_context
return ctx if ctx
# load available certs
cert = Puppet::X509::CertProvider.new
ssl = Puppet::SSL::SSLProvider.new
begin
password = cert.load_private_key_password
ssl.load_context(certname: Puppet[:certname], password: password)
rescue Puppet::SSL::SSLError => e
Puppet.log_exception(e)
# if we don't have cacerts, then create a root context that doesn't
# trust anything. The old code used to fallback to VERIFY_NONE,
# which we don't want to emulate.
ssl.create_root_context(cacerts: [])
end
end
def to_url(path)
if path =~ /^https?:\/\//
# The old Connection class accepts a URL as the request path, and sends
# it in "absolute-form" in the request line, e.g. GET https://puppet:8140/.
# See https://httpwg.org/specs/rfc7230.html#absolute-form. It just so happens
# to work because HTTP 1.1 servers are required to accept absolute-form even
# though clients are only supposed to send them to proxies, so the proxy knows
# what upstream server to CONNECT to. This method creates a URL using the
# scheme/host/port that the connection was created with, and appends the path
# and query portions of the absolute-form. The resulting request will use "origin-form"
# as it should have done all along.
abs_form = URI(path)
url = URI("#{@site.addr}/#{normalize_path(abs_form.path)}")
url.query = abs_form.query if abs_form.query
url
else
URI("#{@site.addr}/#{normalize_path(path)}")
end
end
def normalize_path(path)
if path[0] == '/'
path[1..-1]
else
path
end
end
def with_error_handling(&block)
yield
rescue Puppet::HTTP::TooManyRedirects => e
raise Puppet::Network::HTTP::RedirectionLimitExceededException.new(_("Too many HTTP redirections for %{host}:%{port}") % { host: @host, port: @port }, e)
rescue Puppet::HTTP::HTTPError => e
Puppet.log_exception(e, e.message)
case e.cause
when Net::OpenTimeout, Net::ReadTimeout, Net::HTTPError, EOFError
raise e.cause
else
raise e
end
end
end
Copyright 2K16 - 2K18 Indonesian Hacker Rulez