#-- # Author:: Adam Jacob () # Author:: Thom May () # Author:: Nuo Yan () # Author:: Christopher Brown () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 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. # autoload :URI, "uri" module Net autoload :HTTP, "net/http" end require_relative "ssl_policies" require_relative "http_request" class Chef class HTTP class BasicClient HTTPS = "https".freeze attr_reader :url attr_reader :ssl_policy attr_reader :keepalives attr_reader :nethttp_opts # Instantiate a BasicClient. # === Arguments: # url:: An URI for the remote server. # === Options: # ssl_policy:: The SSL Policy to use, defaults to DefaultSSLPolicy def initialize(url, ssl_policy: DefaultSSLPolicy, keepalives: false, nethttp_opts: {}) @url = url @ssl_policy = ssl_policy @keepalives = keepalives @nethttp_opts = ChefUtils::Mash.new(nethttp_opts) end def http_client @http_client ||= build_http_client end def host @url.hostname end def port @url.port end def request(method, url, req_body, base_headers = {}) http_request = HTTPRequest.new(method, url, req_body, base_headers).http_request Chef::Log.trace("Initiating #{method} to #{url}") Chef::Log.trace("---- HTTP Request Header Data: ----") base_headers.each do |name, value| Chef::Log.trace("#{name}: #{value}") end Chef::Log.trace("---- End HTTP Request Header Data ----") http_client.request(http_request) do |response| Chef::Log.trace("---- HTTP Status and Header Data: ----") Chef::Log.trace("HTTP #{response.http_version} #{response.code} #{response.msg}") response.each do |header, value| Chef::Log.trace("#{header}: #{value}") end Chef::Log.trace("---- End HTTP Status/Header Data ----") # For non-400's, log the request and response bodies if !response.code || !response.code.start_with?("2") if response.body Chef::Log.trace("---- HTTP Response Body ----") Chef::Log.trace(response.body) Chef::Log.trace("---- End HTTP Response Body -----") end if req_body Chef::Log.trace("---- HTTP Request Body ----") Chef::Log.trace(req_body) Chef::Log.trace("---- End HTTP Request Body ----") end end yield response if block_given? # http_client.request may not have the return signature we want, so # force the issue: return [http_request, response] end rescue OpenSSL::SSL::SSLError => e Chef::Log.error("SSL Validation failure connecting to host: #{host} - #{e.message}") raise end def proxy_uri @proxy_uri ||= Chef::Config.proxy_uri(url.scheme, host, port) end def build_http_client # Note: the last nil in the new below forces Net::HTTP to ignore the # no_proxy environment variable. This is a workaround for limitations # in Net::HTTP use of the no_proxy environment variable. We internally # match no_proxy with a fuzzy matcher, rather than letting Net::HTTP # do it. http_client = http_client_builder.new(host, port, nil) http_client.proxy_port = nil if http_client.proxy_address.nil? if url.scheme == HTTPS configure_ssl(http_client) end opts = nethttp_opts.dup opts["read_timeout"] ||= config[:rest_timeout] opts["open_timeout"] ||= config[:rest_timeout] opts.each do |key, value| http_client.send(:"#{key}=", value) end if keepalives http_client.start else http_client end end def config Chef::Config end def http_client_builder if proxy_uri.nil? Net::HTTP else Chef::Log.trace("Using #{proxy_uri.host}:#{proxy_uri.port} for proxy") Net::HTTP.Proxy(proxy_uri.host, proxy_uri.port, http_proxy_user(proxy_uri), http_proxy_pass(proxy_uri)) end end def http_proxy_user(proxy_uri) proxy_uri.user || config["#{proxy_uri.scheme}_proxy_user"] end def http_proxy_pass(proxy_uri) proxy_uri.password || config["#{proxy_uri.scheme}_proxy_pass"] end def configure_ssl(http_client) http_client.use_ssl = true ssl_policy.apply_to(http_client) end end end end