# class Chef::HTTP

## Chef::HTTP¶ ↑

Basic HTTP client, with support for adding features via middleware

### Attributes

middlewares[R]
redirect_limit[R]
sign_on_redirect[R]
url[R]

### Public Class Methods

middlewares() click to toggle source

    # File lib/chef/http.rb, line 64
    def self.middlewares
      @middlewares ||= []
    end

new(url, options={}) click to toggle source

Create a HTTP client object. The supplied `url` is used as the base for all subsequent requests. For example, when initialized with a base url localhost:4000, a call to `get` with ‘nodes’ will make an HTTP GET request to localhost:4000/nodes

    # File lib/chef/http.rb, line 82
    def initialize(url, options={})
      @url = url
      @default_headers = options[:headers] || {}
      @sign_on_redirect = true
      @redirects_followed = 0
      @redirect_limit = 10

      @middlewares = []
      self.class.middlewares.each do |middleware_class|
        @middlewares << middleware_class.new(options)
      end
    end

use(middleware_class) click to toggle source

    # File lib/chef/http.rb, line 68
    def self.use(middleware_class)
      middlewares << middleware_class
    end

### Public Instance Methods

create_url(path) click to toggle source

    # File lib/chef/http.rb, line 203
    def create_url(path)
      return path if path.is_a?(URI)
      if path =~ /^(http|https):\/\//
        URI.parse(path)
      elsif path.nil? or path.empty?
        URI.parse(@url)
      else
        URI.parse("#{@url}/#{path}")
      end
    end

delete(path, headers={}) click to toggle source

Send an HTTP DELETE request to the path

### Parameters¶ ↑

path

  path part of the request URL

    # File lib/chef/http.rb, line 131
    def delete(path, headers={})
      request(:DELETE, path, headers)
    end

get(path, headers={}) click to toggle source

Send an HTTP GET request to the path

### Parameters¶ ↑

path

  The path to GET

    # File lib/chef/http.rb, line 107
    def get(path, headers={})
      request(:GET, path, headers)
    end

head(path, headers={}) click to toggle source

Send an HTTP HEAD request to the path

### Parameters¶ ↑

path

  path part of the request URL

    # File lib/chef/http.rb, line 99
    def head(path, headers={})
      request(:HEAD, path, headers)
    end

http_client(base_url=nil) click to toggle source

    # File lib/chef/http.rb, line 196
    def http_client(base_url=nil)
      base_url ||= url
      BasicClient.new(base_url)
    end

last_response() click to toggle source

This is only kept around to provide access to cache control data in lib/chef/provider/remote_file/http.rb Find a better API.

    # File lib/chef/http.rb, line 388
    def last_response
      @last_response
    end

post(path, json, headers={}) click to toggle source

Send an HTTP POST request to the path

### Parameters¶ ↑

path

  path part of the request URL

    # File lib/chef/http.rb, line 123
    def post(path, json, headers={})
      request(:POST, path, headers, json)
    end

put(path, json, headers={}) click to toggle source

Send an HTTP PUT request to the path

### Parameters¶ ↑

path

  path part of the request URL

    # File lib/chef/http.rb, line 115
    def put(path, json, headers={})
      request(:PUT, path, headers, json)
    end

request(method, path, headers={}, data=false) click to toggle source

Makes an HTTP request to `path` with the given `method`, `headers`, and `data` (if applicable).

    # File lib/chef/http.rb, line 137
    def request(method, path, headers={}, data=false)
      url = create_url(path)
      method, url, headers, data = apply_request_middleware(method, url, headers, data)

      response, rest_request, return_value = send_http_request(method, url, headers, data)
      response, rest_request, return_value = apply_response_middleware(response, rest_request, return_value)
      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

streaming_request(path, headers={}) { |tempfile| ... } click to toggle source

Makes a streaming download request, streaming the response body to a tempfile. If a block is given, the tempfile is passed to the block and the tempfile will automatically be unlinked after the block is executed.

If no block is given, the tempfile is returned, which means it’s up to you to unlink the tempfile when you’re done with it.

    # File lib/chef/http.rb, line 160
    def streaming_request(path, headers={}, &block)
      url = create_url(path)
      response, rest_request, return_value = nil, nil, nil
      tempfile = nil

      method = :GET
      method, url, headers, data = apply_request_middleware(method, url, headers, data)

      response, rest_request, return_value = send_http_request(method, url, headers, data) do |http_response|
        if http_response.kind_of?(Net::HTTPSuccess)
          tempfile = stream_to_tempfile(url, http_response)
        end
        apply_stream_complete_middleware(http_response, rest_request, return_value)
      end

      return nil if response.kind_of?(Net::HTTPRedirection)
      unless response.kind_of?(Net::HTTPSuccess)
        response.error!
      end

      if block_given?
        begin
          yield tempfile
        ensure
          tempfile && tempfile.close!
        end
      end
      tempfile
    rescue Exception => e
      log_failed_request(response, return_value) unless response.nil?
      if e.respond_to?(:chef_rest_request=)
        e.chef_rest_request = rest_request
      end
      raise
    end

### Protected Instance Methods

apply_request_middleware(method, url, headers, data) click to toggle source

    # File lib/chef/http.rb, line 214
    def apply_request_middleware(method, url, headers, data)
      middlewares.inject([method, url, headers, data]) do |req_data, middleware|
        Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_request")
        middleware.handle_request(*req_data)
      end
    end

apply_response_middleware(response, rest_request, return_value) click to toggle source

    # File lib/chef/http.rb, line 221
    def apply_response_middleware(response, rest_request, return_value)
      middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware|
        Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_response")
        middleware.handle_response(*res_data)
      end
    end

apply_stream_complete_middleware(response, rest_request, return_value) click to toggle source

    # File lib/chef/http.rb, line 228
    def apply_stream_complete_middleware(response, rest_request, return_value)
      middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware|
        Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_stream_complete")
        middleware.handle_stream_complete(*res_data)
      end
    end

config() click to toggle source

    # File lib/chef/http.rb, line 327
    def config
      Chef::Config
    end

follow_redirect() { || ... } click to toggle source

    # File lib/chef/http.rb, line 331
    def follow_redirect
      raise Chef::Exceptions::RedirectLimitExceeded if @redirects_followed >= redirect_limit
      @redirects_followed += 1
      Chef::Log.debug("Following redirect #{@redirects_followed}/#{redirect_limit}")

      yield
    ensure
      @redirects_followed = 0
    end

http_retry_count() click to toggle source

    # File lib/chef/http.rb, line 323
    def http_retry_count
      config[:http_retry_count]
    end

http_retry_delay() click to toggle source

    # File lib/chef/http.rb, line 319
    def http_retry_delay
      config[:http_retry_delay]
    end

log_failed_request(response, return_value) click to toggle source

    # File lib/chef/http.rb, line 235
    def log_failed_request(response, return_value)
      return_value ||= {}
      error_message = "HTTP Request Returned #{response.code} #{response.message}: "
      error_message << (return_value["error"].respond_to?(:join) ? return_value["error"].join(", ") : return_value["error"].to_s)
      Chef::Log.info(error_message)
    end

retrying_http_errors(url) { || ... } click to toggle source

Wraps an HTTP request with retry logic.

### Arguments¶ ↑

url

  URL of the request, used for error messages

    # File lib/chef/http.rb, line 284
    def retrying_http_errors(url)
      http_attempts = 0
      begin
        http_attempts += 1

        yield

      rescue SocketError, Errno::ETIMEDOUT => e
        e.message.replace "Error connecting to #{url} - #{e.message}"
        raise e
      rescue Errno::ECONNREFUSED
        if http_retry_count - http_attempts + 1 > 0
          Chef::Log.error("Connection refused connecting to #{url}, retry #{http_attempts}/#{http_retry_count}")
          sleep(http_retry_delay)
          retry
        end
        raise Errno::ECONNREFUSED, "Connection refused connecting to #{url}, giving up"
      rescue Timeout::Error
        if http_retry_count - http_attempts + 1 > 0
          Chef::Log.error("Timeout connecting to #{url}, retry #{http_attempts}/#{http_retry_count}")
          sleep(http_retry_delay)
          retry
        end
        raise Timeout::Error, "Timeout connecting to #{url}, giving up"
      rescue Net::HTTPFatalError => e
        if http_retry_count - http_attempts + 1 > 0
          sleep_time = 1 + (2 ** http_attempts) + rand(2 ** http_attempts)
          Chef::Log.error("Server returned error for #{url}, retrying #{http_attempts}/#{http_retry_count} in #{sleep_time}s")
          sleep(sleep_time)
          retry
        end
        raise
      end
    end

send_http_request(method, url, headers, body, &response_handler) click to toggle source

Runs a synchronous HTTP request, with no middleware applied (use request to have the middleware applied). The entire response will be loaded into memory.

    # File lib/chef/http.rb, line 248
    def send_http_request(method, url, headers, body, &response_handler)
      headers = build_headers(method, url, headers, body)

      retrying_http_errors(url) do
        client = http_client(url)
        return_value = nil
        if block_given?
          request, response = client.request(method, url, body, headers, &response_handler)
        else
          request, response = client.request(method, url, body, headers) {|r| r.read_body }
          return_value = response.read_body
        end
        @last_response = response

        if response.kind_of?(Net::HTTPSuccess)
          [response, request, return_value]
        elsif response.kind_of?(Net::HTTPNotModified) # Must be tested before Net::HTTPRedirection because it's subclass.
          [response, request, false]
        elsif redirect_location = redirected_to(response)
          if [:GET, :HEAD].include?(method)
            follow_redirect do
              send_http_request(method, create_url(redirect_location), headers, body, &response_handler)
            end
          else
            raise Exceptions::InvalidRedirect, "#{method} request was redirected from #{url} to #{redirect_location}. Only GET and HEAD support redirects."
          end
        else
          [response, request, nil]
        end
      end
    end

success_response?(response) click to toggle source

    # File lib/chef/http.rb, line 242
    def success_response?(response)
      response.kind_of?(Net::HTTPSuccess) || response.kind_of?(Net::HTTPRedirection)
    end