Skip to content

Instantly share code, notes, and snippets.

@mtantawy
Created October 21, 2024 16:43
Show Gist options
  • Select an option

  • Save mtantawy/8883871f0ea762852404283b5eb4dcec to your computer and use it in GitHub Desktop.

Select an option

Save mtantawy/8883871f0ea762852404283b5eb4dcec to your computer and use it in GitHub Desktop.
Redirects follower
# Usage: ruby redirector.rb
# Description: A simple web server that follows redirects for a given URL.
require 'webrick'
require 'net/http'
require 'uri'
require 'timeout'
SERVER_PORT = 8000
class RedirectTracker < WEBrick::HTTPServlet::AbstractServlet
TIMEOUT_SECONDS = 5 # Timeout for each individual request
ITERATION_LIMIT = 10 # Limit on number of redirects to follow to prevent infinite loops
USER_AGENT = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0'
def do_GET(request, response)
if request.query['url']
# Track redirects for submitted URL
redirects = track_redirects(request.query['url'])
display_results(response, redirects)
else
# Show initial form
display_form(response)
end
end
private
def track_redirects(start_url)
redirects = [start_url]
current_url = start_url
begin
ITERATION_LIMIT.times do
uri = URI.parse(current_url)
return redirects unless uri.scheme =~ /^https?$/
http = Net::HTTP.new(uri.host, uri.port)
if uri.scheme == 'https'
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
# Set timeouts for various operations
http.open_timeout = TIMEOUT_SECONDS # Time to open connection
http.read_timeout = TIMEOUT_SECONDS # Time to read data
http.ssl_timeout = TIMEOUT_SECONDS # Time for SSL operations
# Create the full path including query parameters
full_path = uri.path
full_path = '/' if full_path.empty?
full_path += "?#{uri.query}" if uri.query
# Make the request with proper headers
request = Net::HTTP::Get.new(full_path)
request['User-Agent'] = USER_AGENT
request['Accept'] = '*/*'
# Wrap the request in a timeout block
response = Timeout.timeout(TIMEOUT_SECONDS) do
puts "Requesting URL: #{current_url}"
http.request(request)
end
puts "Response code: #{response.code} for URL: #{current_url}"
if response.is_a?(Net::HTTPRedirection)
location = response['location']
# Handle relative redirects
current_url = URI.join(current_url, location).to_s
redirects << current_url
else
break
end
end
rescue Timeout::Error
redirects << "(Timeout reached at this URL - assuming final destination)"
rescue StandardError => e
redirects << "Error: #{e.message}"
end
redirects
end
def display_form(response)
response.status = 200
response.content_type = 'text/html'
response.body = <<~HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>URL Redirect Tracker</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; }
form { margin: 20px 0; }
input[type="text"] { width: 100%; padding: 8px; margin: 10px 0; }
input[type="submit"] { padding: 8px 16px; background: #007bff; color: white; border: none; cursor: pointer; }
input[type="submit"]:hover { background: #0056b3; }
</style>
</head>
<body>
<h1>URL Redirect Tracker</h1>
<form method="GET">
<label for="url">Enter URL to track redirects:</label><br>
<input type="text" id="url" name="url" required
placeholder="https://example.com" pattern="https?://.*"><br>
<input type="submit" value="Track Redirects">
</form>
</body>
</html>
HTML
end
def display_results(response, redirects)
response.status = 200
response.content_type = 'text/html'
links_html = redirects.map.with_index do |url, index|
prefix = case index
when 0 then "Initial URL"
when redirects.length - 1 then "Final URL"
else "Redirect #{index}"
end
if url.start_with?("Error:") || url.start_with?("(Timeout")
<<~HTML
<li>
<strong>#{prefix}:</strong>
<span class="error">#{url}</span>
</li>
HTML
else
<<~HTML
<li>
<strong>#{prefix}:</strong>
<a href="#{url}" target="_blank" rel="noopener noreferrer">#{url}</a>
</li>
HTML
end
end.join("\n")
response.body = <<~HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Redirect Results</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; }
ul { list-style-type: none; padding: 0; }
li { margin: 10px 0; word-wrap: break-word; }
a { color: #007bff; }
.error { color: #dc3545; }
.back-link { margin-top: 20px; }
</style>
</head>
<body>
<h1>Redirect Results</h1>
<ul>
#{links_html}
</ul>
<div class="back-link">
<a href="/">Track Another URL</a>
</div>
</body>
</html>
HTML
end
end
# Start the server
server = WEBrick::HTTPServer.new(Port: SERVER_PORT)
server.mount '/', RedirectTracker
# Handle graceful shutdown
trap('INT') { server.shutdown }
server.start
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment