class Puppet::Network::HTTP::Connection
This class provides simple methods for issuing various types of HTTP requests. It's interface is intended to mirror Ruby's Net::HTTP object, but it provides a few important bits of additional functionality. Notably:
-
Any HTTPS requests made using this class will use Puppet's SSL certificate configuration for their authentication, and
-
Provides some useful error handling for any SSL errors that occur during a request.
@deprecated Use {Puppet.runtime} @api public
Constants
- OPTION_DEFAULTS
Public Class Methods
Creates a new HTTP client connection to `host`:`port`. @param host [String] the host to which this client will connect to @param port [Integer] the port to which this client will connect to @param options [Hash] options influencing the properties of the created
connection,
@option options [Boolean] :use_ssl true to connect with SSL, false
otherwise, defaults to true
@option options [Puppet::SSL::Verifier] :verifier An object that will configure
any verification to do on the connection
@option options [Integer] :redirect_limit the number of allowed
redirections, defaults to 10 passing any other option in the options hash results in a Puppet::Error exception
@note the HTTP connection itself happens lazily only when {#request}, or
one of the {#get}, {#post}, {#delete}, {#head} or {#put} is called
@note The correct way to obtain a connection is to use one of the factory
methods on {Puppet::Network::HttpPool}
@api private
# File lib/puppet/network/http/connection.rb 45 def initialize(host, port, options = {}) 46 unknown_options = options.keys - OPTION_DEFAULTS.keys 47 raise Puppet::Error, _("Unrecognized option(s): %{opts}") % { opts: unknown_options.map(&:inspect).sort.join(', ') } unless unknown_options.empty? 48 49 options = OPTION_DEFAULTS.merge(options) 50 @use_ssl = options[:use_ssl] 51 if @use_ssl 52 unless options[:verifier].is_a?(Puppet::SSL::Verifier) 53 raise ArgumentError, _("Expected an instance of Puppet::SSL::Verifier but was passed a %{klass}") % { klass: options[:verifier].class } 54 end 55 56 @verifier = options[:verifier] 57 end 58 @redirect_limit = options[:redirect_limit] 59 @site = Puppet::HTTP::Site.new(@use_ssl ? 'https' : 'http', host, port) 60 @client = Puppet.runtime[:http] 61 end
Public Instance Methods
The address to connect to.
# File lib/puppet/network/http/connection.rb 64 def address 65 @site.host 66 end
@param path [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 141 def delete(path, headers = {'Depth' => 'Infinity'}, options = {}) 142 headers ||= {} 143 options[:ssl_context] ||= resolve_ssl_context 144 options[:redirect_limit] ||= @redirect_limit 145 146 with_error_handling do 147 to_ruby_response(@client.delete(to_url(path), headers: headers, options: options)) 148 end 149 end
@param path [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 96 def get(path, headers = {}, options = {}) 97 headers ||= {} 98 options[:ssl_context] ||= resolve_ssl_context 99 options[:redirect_limit] ||= @redirect_limit 100 101 with_error_handling do 102 to_ruby_response(@client.get(to_url(path), headers: headers, options: options)) 103 end 104 end
@param path [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 127 def head(path, headers = {}, options = {}) 128 headers ||= {} 129 options[:ssl_context] ||= resolve_ssl_context 130 options[:redirect_limit] ||= @redirect_limit 131 132 with_error_handling do 133 to_ruby_response(@client.head(to_url(path), headers: headers, options: options)) 134 end 135 end
The port to connect to.
# File lib/puppet/network/http/connection.rb 69 def port 70 @site.port 71 end
@param path [String] @param data [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 111 def post(path, data, headers = nil, options = {}) 112 headers ||= {} 113 headers['Content-Type'] ||= "application/x-www-form-urlencoded" 114 data ||= '' 115 options[:ssl_context] ||= resolve_ssl_context 116 options[:redirect_limit] ||= @redirect_limit 117 118 with_error_handling do 119 to_ruby_response(@client.post(to_url(path), data, headers: headers, options: options)) 120 end 121 end
@param path [String] @param data [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 156 def put(path, data, headers = nil, options = {}) 157 headers ||= {} 158 headers['Content-Type'] ||= "application/x-www-form-urlencoded" 159 data ||= '' 160 options[:ssl_context] ||= resolve_ssl_context 161 options[:redirect_limit] ||= @redirect_limit 162 163 with_error_handling do 164 to_ruby_response(@client.put(to_url(path), data, headers: headers, options: options)) 165 end 166 end
# File lib/puppet/network/http/connection.rb 168 def request_get(*args, &block) 169 path, headers = *args 170 headers ||= {} 171 options = { 172 ssl_context: resolve_ssl_context, 173 redirect_limit: @redirect_limit 174 } 175 176 ruby_response = nil 177 @client.get(to_url(path), headers: headers, options: options) do |response| 178 ruby_response = to_ruby_response(response) 179 yield ruby_response if block_given? 180 end 181 ruby_response 182 end
# File lib/puppet/network/http/connection.rb 184 def request_head(*args, &block) 185 path, headers = *args 186 headers ||= {} 187 options = { 188 ssl_context: resolve_ssl_context, 189 redirect_limit: @redirect_limit 190 } 191 192 response = @client.head(to_url(path), headers: headers, options: options) 193 ruby_response = to_ruby_response(response) 194 yield ruby_response if block_given? 195 ruby_response 196 end
# File lib/puppet/network/http/connection.rb 198 def request_post(*args, &block) 199 path, data, headers = *args 200 headers ||= {} 201 headers['Content-Type'] ||= "application/x-www-form-urlencoded" 202 options = { 203 ssl_context: resolve_ssl_context, 204 redirect_limit: @redirect_limit 205 } 206 207 ruby_response = nil 208 @client.post(to_url(path), data, headers: headers, options: options) do |response| 209 ruby_response = to_ruby_response(response) 210 yield ruby_response if block_given? 211 end 212 ruby_response 213 end
Whether to use ssl
# File lib/puppet/network/http/connection.rb 74 def use_ssl? 75 @site.use_ssl? 76 end
@api private
# File lib/puppet/network/http/connection.rb 79 def verifier 80 @verifier 81 end
Private Instance Methods
# File lib/puppet/network/http/connection.rb 265 def normalize_path(path) 266 if path[0] == '/' 267 path[1..-1] 268 else 269 path 270 end 271 end
Resolve the ssl_context based on the verifier associated with this connection or load the available set of certs and key on disk. Don't try to bootstrap the agent, as we only want that to be triggered when running `puppet ssl` or `puppet agent`.
# File lib/puppet/network/http/connection.rb 221 def resolve_ssl_context 222 # don't need an ssl context for http connections 223 return nil unless @site.use_ssl? 224 225 # if our verifier has an ssl_context, use that 226 ctx = @verifier.ssl_context 227 return ctx if ctx 228 229 # load available certs 230 cert = Puppet::X509::CertProvider.new 231 ssl = Puppet::SSL::SSLProvider.new 232 begin 233 password = cert.load_private_key_password 234 ssl.load_context(certname: Puppet[:certname], password: password) 235 rescue Puppet::SSL::SSLError => e 236 Puppet.log_exception(e) 237 238 # if we don't have cacerts, then create a root context that doesn't 239 # trust anything. The old code used to fallback to VERIFY_NONE, 240 # which we don't want to emulate. 241 ssl.create_root_context(cacerts: []) 242 end 243 end
# File lib/puppet/network/http/connection.rb 245 def to_url(path) 246 if path =~ /^https?:\/\// 247 # The old Connection class accepts a URL as the request path, and sends 248 # it in "absolute-form" in the request line, e.g. GET https://puppet:8140/. 249 # See https://httpwg.org/specs/rfc7230.html#absolute-form. It just so happens 250 # to work because HTTP 1.1 servers are required to accept absolute-form even 251 # though clients are only supposed to send them to proxies, so the proxy knows 252 # what upstream server to CONNECT to. This method creates a URL using the 253 # scheme/host/port that the connection was created with, and appends the path 254 # and query portions of the absolute-form. The resulting request will use "origin-form" 255 # as it should have done all along. 256 abs_form = URI(path) 257 url = URI("#{@site.addr}/#{normalize_path(abs_form.path)}") 258 url.query = abs_form.query if abs_form.query 259 url 260 else 261 URI("#{@site.addr}/#{normalize_path(path)}") 262 end 263 end
# File lib/puppet/network/http/connection.rb 273 def with_error_handling(&block) 274 yield 275 rescue Puppet::HTTP::TooManyRedirects => e 276 raise Puppet::Network::HTTP::RedirectionLimitExceededException.new(_("Too many HTTP redirections for %{host}:%{port}") % { host: @host, port: @port }, e) 277 rescue Puppet::HTTP::HTTPError => e 278 Puppet.log_exception(e, e.message) 279 case e.cause 280 when Net::OpenTimeout, Net::ReadTimeout, Net::HTTPError, EOFError 281 raise e.cause 282 else 283 raise e 284 end 285 end