module Okay::HTTP

A wrapper around Net::HTTP, focused on ease of use and flexibility.

Constants

DEFAULT_REDIRECT_LIMIT
RedirectLimitError

Public Class Methods

get(url, parameters: {}, headers: {}) click to toggle source

Make an HTTP GET request.

@param url [String] The URL to request. @param parameters [Hash] A hash representing a query string.

# File lib/okay/http.rb, line 56
def self.get(url, parameters: {}, headers: {})
  send_request(:Get, url, parameters, nil, headers)
end
post(url, data: nil, form_data: nil, headers: {}) click to toggle source

Make an HTTP POST request.

@param url [String] The URL to request. @param data [String] Raw data to for the body of the request. @param form_data [Hash] Data for the request body, encoded as though it

were a form.
# File lib/okay/http.rb, line 66
def self.post(url, data: nil, form_data: nil, headers: {})
  if !data.nil? && !form_data.nil?
    raise ArgumentError,
          "cannot specify data and form_data arguments simultaneously."
  end

  if form_data.nil?
    body = data
  else
    body = URI.encode_www_form(form_data)
  end

  send_request(:Post, url, nil, body, headers)
end

Private Class Methods

send_request(http_method, url, parameters, body, headers, redirect_limit = DEFAULT_REDIRECT_LIMIT) click to toggle source

Helper method for actually creating a request.

@param http_method [Symbol] A symbol representing the class name for

+Net::HTTP+. E.g., +:Get+ for GET requests, +:Post+ for POST, etc.

@param url [String] URL for the request. @param parameters [Hash, nil] Request parameters (for the query string). @param body [String, nil] Request body. @param redirect_limit [Numeric] The maximum number of redirects allowed.

# File lib/okay/http.rb, line 91
def self.send_request(http_method, url, parameters, body, headers,
                      redirect_limit = DEFAULT_REDIRECT_LIMIT)
  if redirect_limit <= 0
    raise RedirectLimitError, "request exceeded redirect limit"
  end

  # Convert the URL (a String) into a URI object.
  uri = URI.parse(url)

  # Set the query string for the request.
  unless parameters.nil? || parameters.empty?
    uri.query = URI.encode_www_form(parameters)
  end

  options = {
    # If the URI starts with "https://", enable SSL/TLS.
    use_ssl: (uri.scheme == "https"),
  }

  # Net::HTTP.start() keeps a connection to the host alive
  # for all requests that occur inside the block.
  Net::HTTP.start(uri.host, uri.port, options) do |http|
    # Get a reference to the class for the specified request type.
    # E.g., if it's a post request, this returns Net::HTTP::Post.
    request_class = Net::HTTP.const_get(http_method)

    # Create the request object, but don't send it.
    request = request_class.new(uri)

    headers.each do |k, v|
      request[k] = v
    end

    # Set the request body, if there is one.
    request.body = body unless body.nil?

    # Send the request, storing the result in +response+.
    response = http.request(request)

    # Handle responses.
    if response.is_a?(Net::HTTPRedirection)
      # Follow a redirect.
      # Decrements +redirect_limit+ while doing so, to avoid redirect loops.
      # NOTE: Does not handle HTTP 307. https://httpstatuses.com/307
      new_url = response["location"]
      if new_url.start_with?("/")
        new_uri = uri.dup
        new_uri.path = response["location"]
        new_url = new_uri.to_s
      elsif new_url.start_with?(".")
        new_uri = uri.dup
        new_uri.path += "/" + response["location"]
        new_url = new_uri.to_s
      end
      send_request(:Get, new_url, parameters, body, headers,
                   redirect_limit - 1)
    else
      response
    end
  end
end