123456789_123456789_123456789_123456789_123456789_

Class: Octokit::Middleware::FollowRedirects

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Faraday::Middleware
Instance Chain:
self, Faraday::Middleware
Inherits: Faraday::Middleware
  • Object
Defined in: lib/octokit/middleware/follow_redirects.rb

Overview

Public: Follow HTTP 301, 302, 303, and 307 redirects.

For HTTP 303, the original GET, POST, PUT, DELETE, or PATCH request gets converted into a GET. For HTTP 301, 302, and 307, the HTTP method remains unchanged.

This middleware currently only works with synchronous requests; i.e. it doesn't support parallelism.

Constant Summary

Class Method Summary

Instance Method Summary

Constructor Details

.new(app, options = {}) ⇒ FollowRedirects

Public: Initialize the middleware.

options - An options Hash (default: {}): :limit - A Integer redirect limit (default: 3).

[ GitHub ]

  
# File 'lib/octokit/middleware/follow_redirects.rb', line 53

def initialize(app, options = {})
  super(app)
  @options = options

  @convert_to_get = Set.new [303]
end

Instance Method Details

#call(env)

[ GitHub ]

  
# File 'lib/octokit/middleware/follow_redirects.rb', line 60

def call(env)
  perform_with_redirection(env, follow_limit)
end

#convert_to_get?(response) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/octokit/middleware/follow_redirects.rb', line 66

def convert_to_get?(response)
  !%i[head options].include?(response.env[:method]) &&
    @convert_to_get.include?(response.status)
end

#follow_limit (private)

[ GitHub ]

  
# File 'lib/octokit/middleware/follow_redirects.rb', line 113

def follow_limit
  @options.fetch(:limit, FOLLOW_LIMIT)
end

#follow_redirect?(env, response) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/octokit/middleware/follow_redirects.rb', line 108

def follow_redirect?(env, response)
  ALLOWED_METHODS.include?(env[:method]) &&
    REDIRECT_CODES.include?(response.status)
end

#perform_with_redirection(env, follows) (private)

[ GitHub ]

  
# File 'lib/octokit/middleware/follow_redirects.rb', line 71

def perform_with_redirection(env, follows)
  request_body = env[:body]
  response = @app.call(env)

  response.on_complete do |response_env|
    if follow_redirect?(response_env, response)
      raise(RedirectLimitReached, response) if follows.zero?

      new_request_env = update_env(response_env, request_body, response)
      response = perform_with_redirection(new_request_env, follows - 1)
    end
  end
  response
end

#safe_escape(uri) (private)

Internal: Escapes unsafe characters from a URL which might be a path component only or a fully-qualified URI so that it can be joined onto a URI:HTTP using the + operator. Doesn't escape "%" characters so to not risk double-escaping.

[ GitHub ]

  
# File 'lib/octokit/middleware/follow_redirects.rb', line 128

def safe_escape(uri)
  uri.to_s.gsub(URI_UNSAFE) do |match|
    '%' + match.unpack('H2' * match.bytesize).join('%').upcase
  end
end

#same_host?(original_url, redirect_url) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/octokit/middleware/follow_redirects.rb', line 117

def same_host?(original_url, redirect_url)
  original_uri = Addressable::URI.parse(original_url)
  redirect_uri = Addressable::URI.parse(redirect_url)

  redirect_uri.host.nil? || original_uri.host == redirect_uri.host
end

#update_env(env, request_body, response) (private)

[ GitHub ]

  
# File 'lib/octokit/middleware/follow_redirects.rb', line 86

def update_env(env, request_body, response)
  original_url = env[:url]
  env[:url] += safe_escape(response['location'])
  unless same_host?(original_url, env[:url])
    # HACK: Faraday’s Authorization middlewares don’t touch the request if the `Authorization` header is set.
    #       This is a workaround to drop authentication info.
    #       See https://github.com/octokit/octokit.rb/pull/1359#issuecomment-925609697
    env[:request_headers]['Authorization'] = 'dummy'
  end

  if convert_to_get?(response)
    env[:method] = :get
    env[:body] = nil
  else
    env[:body] = request_body
  end

  ENV_TO_CLEAR.each { |key| env.delete(key) }

  env
end