FIX: If HEAD is not supported, try GET. Also set cookies

This commit is contained in:
Robin Ward 2017-06-06 13:53:49 -04:00
parent a4be79d297
commit 502bca2c0d
2 changed files with 95 additions and 15 deletions

View File

@ -7,6 +7,7 @@ require 'rate_limiter'
class FinalDestination
attr_reader :status
attr_reader :cookie
def initialize(url, opts=nil)
@uri = URI(url) rescue nil
@ -21,6 +22,11 @@ class FinalDestination
end
@limit = @opts[:max_redirects]
@status = :ready
@cookie = nil
end
def self.connection_timeout
20
end
def redirected?
@ -28,9 +34,27 @@ class FinalDestination
end
def request_headers
{ "User-Agent" => "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
result = {
"User-Agent" => "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
"Accept" => "text/html",
"Host" => @uri.hostname }
"Host" => @uri.hostname
}
result['cookie'] = @cookie if @cookie
result
end
def small_get(headers)
Net::HTTP.start(@uri.host, @uri.port, use_ssl: @uri.is_a?(URI::HTTPS)) do |http|
http.open_timeout = FinalDestination.connection_timeout
http.read_timeout = FinalDestination.connection_timeout
request = Net::HTTP::Get.new(@uri.request_uri, headers)
http.request(request) do |response|
return response
end
end
end
def resolve
@ -41,17 +65,41 @@ class FinalDestination
return nil unless validate_uri
headers = request_headers
head = Excon.head(@uri.to_s, read_timeout: 20, headers: headers)
response = Excon.head(
@uri.to_s,
read_timeout: FinalDestination.connection_timeout,
headers: headers
)
# If the site does not allow HEAD, just try the url
return @uri if head.status == 405
if head.status == 200
location = nil
case response.status
when 200
@status = :resolved
return @uri
when 405, 501
get_response = small_get(headers)
if get_response.code.to_i == 200
@status = :resolved
return @uri
end
if cookie_val = get_response.get_fields('set-cookie')
@cookie = cookie_val.join
end
if location_val = get_response.get_fields('location')
location = location_val.join
end
else
response.headers.each do |k, v|
case k.downcase
when 'set-cookie' then @cookie = v
when 'location' then location = v
end
end
end
location = FinalDestination.header_for(head, 'location')
if location
location = "#{@uri.scheme}://#{@uri.host}#{location}" if location[0] == "/"
@uri = URI(location) rescue nil
@ -127,11 +175,4 @@ class FinalDestination
IPSocket::getaddress(host)
end
def self.header_for(head, name)
header = head.headers.detect do |k, _|
name == k.downcase
end
header[1] if header
end
end

View File

@ -120,6 +120,45 @@ describe FinalDestination do
expect(final.status).to eq(:invalid_address)
end
end
context "HEAD not supported" do
before do
stub_request(:get, 'https://eviltrout.com').to_return(
status: 301,
headers: {
"Location" => 'https://discourse.org',
'Set-Cookie' => 'evil=trout'
}
)
stub_request(:head, 'https://discourse.org').to_return(status: 200)
end
context "when the status code is 405" do
before do
stub_request(:head, 'https://eviltrout.com').to_return(status: 405)
end
it "will try a GET" do
final = FinalDestination.new('https://eviltrout.com', opts)
expect(final.resolve.to_s).to eq('https://discourse.org')
expect(final.status).to eq(:resolved)
expect(final.cookie).to eq('evil=trout')
end
end
context "when the status code is 501" do
before do
stub_request(:head, 'https://eviltrout.com').to_return(status: 501)
end
it "will try a GET" do
final = FinalDestination.new('https://eviltrout.com', opts)
expect(final.resolve.to_s).to eq('https://discourse.org')
expect(final.status).to eq(:resolved)
expect(final.cookie).to eq('evil=trout')
end
end
end
end
describe '.validate_uri' do