Created
October 21, 2024 16:43
-
-
Save mtantawy/8883871f0ea762852404283b5eb4dcec to your computer and use it in GitHub Desktop.
Redirects follower
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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