class Micky::Request
Public Class Methods
new(opts = {})
click to toggle source
# File lib/micky/request.rb, line 6 def initialize(opts = {}) # Options can be set per request and fallback to module-level defaults [:raise_errors, :max_redirects, :timeout, :skip_resolve, :resolve_timeout, :oauth, :query, :headers, :parsers].each do |name| value = opts.has_key?(name) ? opts[name] : Micky.public_send(name) instance_variable_set "@#{name}", value end end
Public Instance Methods
get(uri)
click to toggle source
# File lib/micky/request.rb, line 14 def get(uri) @request_class_name = 'Get' request_with_redirect_handling(uri) end
head(uri)
click to toggle source
# File lib/micky/request.rb, line 19 def head(uri) @request_class_name = 'Head' request_with_redirect_handling(uri) end
Private Instance Methods
log(message)
click to toggle source
# File lib/micky/request.rb, line 162 def log(message) message = "#{message.class}: #{message.message}" if message.is_a? Exception warn "Micky.#{@request_class_name.downcase}('#{@uri}'): #{message}" end
request(uri)
click to toggle source
# File lib/micky/request.rb, line 79 def request(uri) @uri = Micky::URI(uri) or begin raise Micky::InvalidURIError, uri if @raise_errors warn "Micky.#{@request_class_name.downcase}('#{uri}'): Invalid URI" return nil end unless @skip_resolve == true # Resolv is the only host validity check that can be wrapped with Timeout. # Net::HTTP and OpenURI use TCPSocket.open which isn’t timeoutable. require 'resolv' unless defined? Resolv begin Timeout.timeout(@resolve_timeout) do begin Resolv::DNS.new.getaddress(@uri.host) rescue Resolv::ResolvError => e raise Micky::HostError, original_exception: e if @raise_errors log 'Host resolution error' return nil end end rescue Timeout::Error => e raise Micky::HostError, "Host resolution timeout: #{@uri}" if @raise_errors log 'Host resolution timeout' return nil end end # Connection http = Net::HTTP.new(@uri.host, @uri.port) http.use_ssl = @uri.scheme == 'https' http.open_timeout = @timeout http.read_timeout = @timeout http.ssl_timeout = @timeout http.write_timeout = @timeout if http.respond_to?(:write_timeout=) # Query string query = Hash[::URI.decode_www_form(@uri.query || '')] if @query && @query.any? query.merge! Hash[@query.map { |k,v| [k.to_s, v] }] @uri.query = ::URI.encode_www_form(query) end # OAuth if @oauth && @oauth.any? unless defined? SimpleOAuth begin require 'simple_oauth' rescue LoadError raise 'You must install the simple_oauth gem to use the :oauth argument.' end end uri_without_query = @uri.dup uri_without_query.query = '' header = SimpleOAuth::Header.new(@request_class_name, uri_without_query, query, @oauth).to_s @headers['Authorization'] = header end # Request request = Net::HTTP.const_get(@request_class_name).new(@uri) # Headers @headers.each { |k,v| request[k] = v } begin http.request(request) rescue Zlib::Error request['Accept-Encoding'] = 'identity' retry rescue Errno::ECONNREFUSED, OpenSSL::SSL::SSLError, SocketError => e raise Micky::ClientError, original_exception: e if @raise_errors log e nil rescue Net::HTTPBadResponse, SystemCallError, IOError, Timeout::Error => e raise Micky::ServerError, original_exception: e if @raise_errors log e nil end end
request_with_redirect_handling(uri, redirect_count = 0)
click to toggle source
# File lib/micky/request.rb, line 26 def request_with_redirect_handling(uri, redirect_count = 0) if redirect_count >= @max_redirects raise Micky::TooManyRedirects, "Max redirects reached (#{@max_redirects})" if @raise_errors log "Max redirects reached (#{@max_redirects})" return nil end case response = request(uri) when Net::HTTPSuccess Response.new(response, @uri) when Net::HTTPRedirection previous_uri = uri uri = response['Location'] if uri.nil? raise Micky::NoRedirectLocation, response: response if @raise_errors warn "Micky.#{@request_class_name.downcase}('#{previous_uri}'): No “Location” for #{response.code} response" return nil end if uri !~ Micky::HTTP_URI_REGEX if uri.start_with? '//' # Protocol-relative uri = Micky::URI(uri).to_s elsif uri.start_with? '/' # Host-relative previous_uri = Micky::URI(previous_uri) uri = File.join("#{previous_uri.scheme}://#{previous_uri.host}", uri) else # Path-relative previous_uri = Micky::URI(previous_uri) previous_directory = previous_uri.path.sub(/[^\/]+\z/, '') uri = File.join("#{previous_uri.scheme}://#{previous_uri.host}#{previous_directory}", uri) end end log "Redirect to #{uri}" request_with_redirect_handling(uri, redirect_count + 1) else if @raise_errors case response when Net::HTTPClientError raise Micky::HTTPClientError, response: response when Net::HTTPServerError raise Micky::HTTPServerError, response: response end else log response nil end end end