#-- # Author:: Adam Jacob () # Author:: Thom May () # Author:: Nuo Yan () # Author:: Christopher Brown () # Author:: Christopher Walters () # Copyright:: Copyright 2009-2016, 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. # require "tempfile" require "chef/http" class Chef class HTTP; end class REST < HTTP; end end require "chef/http/authenticator" require "chef/http/decompressor" require "chef/http/json_input" require "chef/http/json_to_model_output" require "chef/http/cookie_manager" require "chef/http/validate_content_length" require "chef/config" require "chef/exceptions" require "chef/platform/query_helpers" require "chef/http/remote_request_id" require "chef/chef_class" class Chef # == Chef::REST # Chef's custom REST client with built-in JSON support and RSA signed header # authentication. class REST < HTTP # Backwards compatibility for things that use # Chef::REST::RESTRequest or its constants RESTRequest = HTTP::HTTPRequest attr_accessor :url, :cookies, :sign_on_redirect, :redirect_limit attr_reader :authenticator # Create a REST client object. The supplied +url+ is used as the base for # all subsequent requests. For example, when initialized with a base url # http://localhost:4000, a call to +get_rest+ with 'nodes' will make an # HTTP GET request to http://localhost:4000/nodes def initialize(url, client_name = Chef::Config[:node_name], signing_key_filename = Chef::Config[:client_key], options = {}) Chef.deprecated(:chef_rest, "Chef::REST is deprecated. Please use Chef::ServerAPI, or investigate Ridley or ChefAPI.") signing_key_filename = nil if chef_zero_uri?(url) options = options.dup options[:client_name] = client_name options[:signing_key_filename] = signing_key_filename super(url, options) @decompressor = Decompressor.new(options) @authenticator = Authenticator.new(options) @request_id = RemoteRequestID.new(options) @middlewares << JSONInput.new(options) @middlewares << JSONToModelOutput.new(options) @middlewares << CookieManager.new(options) @middlewares << @decompressor @middlewares << @authenticator @middlewares << @request_id # ValidateContentLength should come after Decompressor # because the order of middlewares is reversed when handling # responses. @middlewares << ValidateContentLength.new(options) end def signing_key_filename authenticator.signing_key_filename end def auth_credentials authenticator.auth_credentials end def client_name authenticator.client_name end def signing_key authenticator.raw_key end def sign_requests? authenticator.sign_requests? end # Send an HTTP GET request to the path # # Using this method to +fetch+ a file is considered deprecated. # # === Parameters # path:: The path to GET # raw:: Whether you want the raw body returned, or JSON inflated. Defaults # to JSON inflated. def get(path, raw = false, headers = {}) if raw streaming_request(path, headers) else request(:GET, path, headers) end end alias :get_rest :get alias :delete_rest :delete alias :post_rest :post alias :put_rest :put # Streams a download to a tempfile, then yields the tempfile to a block. # After the download, the tempfile will be closed and unlinked. # If you rename the tempfile, it will not be deleted. # Beware that if the server streams infinite content, this method will # stream it until you run out of disk space. def fetch(path, headers = {}) streaming_request(create_url(path), headers) { |tmp_file| yield tmp_file } end alias :api_request :request # Do a HTTP request where no middleware is loaded (e.g. JSON input/output # conversion) but the standard Chef Authentication headers are added to the # request. def raw_http_request(method, path, headers, data) url = create_url(path) method, url, headers, data = @authenticator.handle_request(method, url, headers, data) method, url, headers, data = @request_id.handle_request(method, url, headers, data) response, rest_request, return_value = send_http_request(method, url, headers, data) response.error! unless success_response?(response) return_value rescue Exception => exception log_failed_request(response, return_value) unless response.nil? if exception.respond_to?(:chef_rest_request=) exception.chef_rest_request = rest_request end raise end # Deprecated: # Responsibilities of this method have been split up. The #http_client is # now responsible for making individual requests, while # #retrying_http_errors handles error/retry logic. def retriable_http_request(method, url, req_body, headers) rest_request = Chef::HTTP::HTTPRequest.new(method, url, req_body, headers) Chef::Log.debug("Sending HTTP request via #{method} to #{url.host}:#{url.port}#{rest_request.path}") retrying_http_errors(url) do yield rest_request end end # Customized streaming behavior; sets the accepted content type to "*/*" # if not otherwise specified for compatibility purposes def streaming_request(url, headers, &block) headers["Accept"] ||= "*/*" super end alias :retriable_rest_request :retriable_http_request def follow_redirect unless @sign_on_redirect @authenticator.sign_request = false end super ensure @authenticator.sign_request = true end public :create_url ############################################################################ # DEPRECATED ############################################################################ def decompress_body(body) @decompressor.decompress_body(body) end def authentication_headers(method, url, json_body = nil) authenticator.authentication_headers(method, url, json_body) end end end