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 46 def initialize(host, port, options = {}) 47 unknown_options = options.keys - OPTION_DEFAULTS.keys 48 raise Puppet::Error, _("Unrecognized option(s): %{opts}") % { opts: unknown_options.map(&:inspect).sort.join(', ') } unless unknown_options.empty? 49 50 options = OPTION_DEFAULTS.merge(options) 51 @use_ssl = options[:use_ssl] 52 if @use_ssl 53 unless options[:verifier].is_a?(Puppet::SSL::Verifier) 54 raise ArgumentError, _("Expected an instance of Puppet::SSL::Verifier but was passed a %{klass}") % { klass: options[:verifier].class } 55 end 56 57 @verifier = options[:verifier] 58 end 59 @redirect_limit = options[:redirect_limit] 60 @site = Puppet::HTTP::Site.new(@use_ssl ? 'https' : 'http', host, port) 61 @client = Puppet.runtime[:http] 62 end
Public Instance Methods
The address to connect to.
# File lib/puppet/network/http/connection.rb 65 def address 66 @site.host 67 end
@param path [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 142 def delete(path, headers = {'Depth' => 'Infinity'}, options = {}) 143 headers ||= {} 144 options[:ssl_context] ||= resolve_ssl_context 145 options[:redirect_limit] ||= @redirect_limit 146 147 with_error_handling do 148 to_ruby_response(@client.delete(to_url(path), headers: headers, options: options)) 149 end 150 end
@param path [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 97 def get(path, headers = {}, options = {}) 98 headers ||= {} 99 options[:ssl_context] ||= resolve_ssl_context 100 options[:redirect_limit] ||= @redirect_limit 101 102 with_error_handling do 103 to_ruby_response(@client.get(to_url(path), headers: headers, options: options)) 104 end 105 end
@param path [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 128 def head(path, headers = {}, options = {}) 129 headers ||= {} 130 options[:ssl_context] ||= resolve_ssl_context 131 options[:redirect_limit] ||= @redirect_limit 132 133 with_error_handling do 134 to_ruby_response(@client.head(to_url(path), headers: headers, options: options)) 135 end 136 end
The port to connect to.
# File lib/puppet/network/http/connection.rb 70 def port 71 @site.port 72 end
@param path [String] @param data [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 112 def post(path, data, headers = nil, options = {}) 113 headers ||= {} 114 headers['Content-Type'] ||= "application/x-www-form-urlencoded" 115 data ||= '' 116 options[:ssl_context] ||= resolve_ssl_context 117 options[:redirect_limit] ||= @redirect_limit 118 119 with_error_handling do 120 to_ruby_response(@client.post(to_url(path), data, headers: headers, options: options)) 121 end 122 end
@param path [String] @param data [String] @param headers [Hash{String => String}] @!macro common_options @api public
# File lib/puppet/network/http/connection.rb 157 def put(path, data, headers = nil, options = {}) 158 headers ||= {} 159 headers['Content-Type'] ||= "application/x-www-form-urlencoded" 160 data ||= '' 161 options[:ssl_context] ||= resolve_ssl_context 162 options[:redirect_limit] ||= @redirect_limit 163 164 with_error_handling do 165 to_ruby_response(@client.put(to_url(path), data, headers: headers, options: options)) 166 end 167 end
# File lib/puppet/network/http/connection.rb 169 def request_get(*args, &block) 170 path, headers = *args 171 headers ||= {} 172 options = { 173 ssl_context: resolve_ssl_context, 174 redirect_limit: @redirect_limit 175 } 176 177 ruby_response = nil 178 @client.get(to_url(path), headers: headers, options: options) do |response| 179 ruby_response = to_ruby_response(response) 180 yield ruby_response if block_given? 181 end 182 ruby_response 183 end
# File lib/puppet/network/http/connection.rb 185 def request_head(*args, &block) 186 path, headers = *args 187 headers ||= {} 188 options = { 189 ssl_context: resolve_ssl_context, 190 redirect_limit: @redirect_limit 191 } 192 193 response = @client.head(to_url(path), headers: headers, options: options) 194 ruby_response = to_ruby_response(response) 195 yield ruby_response if block_given? 196 ruby_response 197 end
# File lib/puppet/network/http/connection.rb 199 def request_post(*args, &block) 200 path, data, headers = *args 201 headers ||= {} 202 headers['Content-Type'] ||= "application/x-www-form-urlencoded" 203 options = { 204 ssl_context: resolve_ssl_context, 205 redirect_limit: @redirect_limit 206 } 207 208 ruby_response = nil 209 @client.post(to_url(path), data, headers: headers, options: options) do |response| 210 ruby_response = to_ruby_response(response) 211 yield ruby_response if block_given? 212 end 213 ruby_response 214 end
Whether to use ssl
# File lib/puppet/network/http/connection.rb 75 def use_ssl? 76 @site.use_ssl? 77 end
@api private
# File lib/puppet/network/http/connection.rb 80 def verifier 81 @verifier 82 end
Private Instance Methods
# File lib/puppet/network/http/connection.rb 266 def normalize_path(path) 267 if path[0] == '/' 268 path[1..-1] 269 else 270 path 271 end 272 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 222 def resolve_ssl_context 223 # don't need an ssl context for http connections 224 return nil unless @site.use_ssl? 225 226 # if our verifier has an ssl_context, use that 227 ctx = @verifier.ssl_context 228 return ctx if ctx 229 230 # load available certs 231 cert = Puppet::X509::CertProvider.new 232 ssl = Puppet::SSL::SSLProvider.new 233 begin 234 password = cert.load_private_key_password 235 ssl.load_context(certname: Puppet[:certname], password: password) 236 rescue Puppet::SSL::SSLError => e 237 Puppet.log_exception(e) 238 239 # if we don't have cacerts, then create a root context that doesn't 240 # trust anything. The old code used to fallback to VERIFY_NONE, 241 # which we don't want to emulate. 242 ssl.create_root_context(cacerts: []) 243 end 244 end
# File lib/puppet/network/http/connection.rb 246 def to_url(path) 247 if path =~ /^https?:\/\// 248 # The old Connection class accepts a URL as the request path, and sends 249 # it in "absolute-form" in the request line, e.g. GET https://puppet:8140/. 250 # See https://httpwg.org/specs/rfc7230.html#absolute-form. It just so happens 251 # to work because HTTP 1.1 servers are required to accept absolute-form even 252 # though clients are only supposed to send them to proxies, so the proxy knows 253 # what upstream server to CONNECT to. This method creates a URL using the 254 # scheme/host/port that the connection was created with, and appends the path 255 # and query portions of the absolute-form. The resulting request will use "origin-form" 256 # as it should have done all along. 257 abs_form = URI(path) 258 url = URI("#{@site.addr}/#{normalize_path(abs_form.path)}") 259 url.query = abs_form.query if abs_form.query 260 url 261 else 262 URI("#{@site.addr}/#{normalize_path(path)}") 263 end 264 end
# File lib/puppet/network/http/connection.rb 274 def with_error_handling(&block) 275 yield 276 rescue Puppet::HTTP::TooManyRedirects => e 277 raise Puppet::Network::HTTP::RedirectionLimitExceededException.new(_("Too many HTTP redirections for %{host}:%{port}") % { host: @host, port: @port }, e) 278 rescue Puppet::HTTP::HTTPError => e 279 Puppet.log_exception(e, e.message) 280 case e.cause 281 when Net::OpenTimeout, Net::ReadTimeout, Net::HTTPError, EOFError 282 raise e.cause 283 else 284 raise e 285 end 286 end