|
require 'faraday'
require 'faraday_middleware'
module WebRequestConcern
module DoNotEncoder
def self.encode(params)
params.map do |key, value|
value.nil? ? "#{key}" : "#{key}=#{value}"
end.join('&')
end
def self.decode(val)
[val]
end
end
class CharacterEncoding < Faraday::Middleware
def initialize(app, force_encoding: nil, default_encoding: nil, unzip: nil)
super(app)
@force_encoding = force_encoding
@default_encoding = default_encoding
@unzip = unzip
end
def call(env)
@app.call(env).on_complete do |env|
body = env[:body]
case @unzip
when 'gzip'.freeze
body.replace(ActiveSupport::Gzip.decompress(body))
end
case
when @force_encoding
encoding = @force_encoding
when body.encoding == Encoding::ASCII_8BIT
# Not all Faraday adapters support automatic charset
# detection, so we do that.
case env[:response_headers][:content_type]
when /;\s*charset\s*=\s*([^()<>@,;:\\\"\/\[\]?={}\s]+)/i
encoding = Encoding.find($1) rescue nil
when /\A\s*(?:text\/[^\s;]+|application\/(?:[^\s;]+\+)?(?:xml|json))\s*(?:;|\z)/i
encoding = @default_encoding
else
# Never try to transcode a binary content
next
end
end
body.encode!(Encoding::UTF_8, encoding) unless body.encoding == Encoding::UTF_8
end
end
end
Faraday::Response.register_middleware character_encoding: CharacterEncoding
extend ActiveSupport::Concern
def validate_web_request_options!
if options['user_agent'].present?
errors.add(:base, "user_agent must be a string") unless options['user_agent'].is_a?(String)
end
if options['disable_ssl_verification'].present? && boolify(options['disable_ssl_verification']).nil?
errors.add(:base, "if provided, disable_ssl_verification must be true or false")
end
unless headers(options['headers']).is_a?(Hash)
errors.add(:base, "if provided, headers must be a hash")
end
begin
basic_auth_credentials(options['basic_auth'])
rescue ArgumentError => e
errors.add(:base, e.message)
end
if (encoding = options['force_encoding']).present?
case encoding
when String
begin
Encoding.find(encoding)
rescue ArgumentError
errors.add(:base, "Unknown encoding: #{encoding.inspect}")
end
else
errors.add(:base, "force_encoding must be a string")
end
end
end
def default_encoding
Encoding::UTF_8
end
def faraday
faraday_options = {
ssl: {
verify: !boolify(options['disable_ssl_verification'])
}
}
@faraday ||= Faraday.new(faraday_options) { |builder|
builder.response :character_encoding,
force_encoding: interpolated['force_encoding'].presence,
default_encoding: default_encoding,
unzip: interpolated['unzip'].presence
builder.headers = headers if headers.length > 0
builder.headers[:user_agent] = user_agent
builder.use FaradayMiddleware::FollowRedirects
builder.request :url_encoded
if boolify(interpolated['disable_url_encoding'])
builder.options.params_encoder = DoNotEncoder
end
if userinfo = basic_auth_credentials
builder.request :basic_auth, *userinfo
end
builder.use FaradayMiddleware::Gzip
unless builder.headers.any? { |key,| /\Aaccept[-_]encoding\z/i =~ key }
# Exclude `deflate` by default. See #1018.
builder.headers[:accept_encoding] = 'gzip,identity'
end
case backend = faraday_backend
when :typhoeus
require 'typhoeus/adapters/faraday'
end
builder.adapter backend
}
end
def headers(value = interpolated['headers'])
value.presence || {}
end
def basic_auth_credentials(value = interpolated['basic_auth'])
case value
when nil, ''
return nil
when Array
return value if value.size == 2
when /:/
return value.split(/:/, 2)
end
raise ArgumentError.new("bad value for basic_auth: #{value.inspect}")
end
def faraday_backend
ENV.fetch('FARADAY_HTTP_BACKEND', 'typhoeus').to_sym
end
def user_agent
interpolated['user_agent'].presence || self.class.default_user_agent
end
module ClassMethods
def default_user_agent
ENV.fetch('DEFAULT_HTTP_USER_AGENT', "Huginn - https://github.com/cantino/huginn")
end
end
end
|