class Rack::Prerender
Public Class Methods
new(app, options={})
click to toggle source
# File lib/prerender_rails.rb, line 6 def initialize(app, options={}) @connection_header_values = [ 'close', 'keep-alive' ].freeze @hop_by_hop_headers = [ 'Connection', 'Keep-Alive', 'Public', 'Proxy-Authenticate', 'Transfer-Encoding', 'Upgrade' ].freeze @crawler_user_agents = [ 'googlebot', 'Google-InspectionTool', 'yahoo', 'bingbot', 'baiduspider', 'facebookexternalhit', 'twitterbot', 'rogerbot', 'linkedinbot', 'embedly', 'bufferbot', 'quora link preview', 'showyoubot', 'outbrain', 'pinterest/0.', 'developers.google.com/+/web/snippet', 'www.google.com/webmasters/tools/richsnippets', 'slackbot', 'vkShare', 'W3C_Validator', 'redditbot', 'Applebot', 'WhatsApp', 'flipboard', 'tumblr', 'bitlybot', 'SkypeUriPreview', 'nuzzel', 'Discordbot', 'Google Page Speed', 'Qwantify', 'Chrome-Lighthouse', 'TelegramBot' ] @extensions_to_ignore = [ '.js', '.css', '.xml', '.less', '.png', '.jpg', '.jpeg', '.gif', '.pdf', '.doc', '.txt', '.ico', '.rss', '.zip', '.mp3', '.rar', '.exe', '.wmv', '.doc', '.avi', '.ppt', '.mpg', '.mpeg', '.tif', '.wav', '.mov', '.psd', '.ai', '.xls', '.mp4', '.m4a', '.swf', '.dat', '.dmg', '.iso', '.flv', '.m4v', '.torrent' ] @options = options @options[:whitelist] = [@options[:whitelist]] if @options[:whitelist].is_a? String @options[:blacklist] = [@options[:blacklist]] if @options[:blacklist].is_a? String @extensions_to_ignore = @options[:extensions_to_ignore] if @options[:extensions_to_ignore] @crawler_user_agents = @options[:crawler_user_agents] if @options[:crawler_user_agents] @app = app end
Public Instance Methods
after_render(env, response)
click to toggle source
# File lib/prerender_rails.rb, line 266 def after_render(env, response) return true unless @options[:after_render] @options[:after_render].call(env, response) end
before_render(env)
click to toggle source
# File lib/prerender_rails.rb, line 251 def before_render(env) return nil unless @options[:before_render] cached_render = @options[:before_render].call(env) if cached_render && cached_render.is_a?(String) Rack::Response.new(cached_render, 200, { 'Content-Type' => 'text/html; charset=utf-8' }) elsif cached_render && cached_render.is_a?(Rack::Response) cached_render else nil end end
build_api_url(env)
click to toggle source
# File lib/prerender_rails.rb, line 213 def build_api_url(env) new_env = env if env["CF-VISITOR"] match = /"scheme":"(http|https)"/.match(env['CF-VISITOR']) new_env["HTTPS"] = true and new_env["rack.url_scheme"] = "https" and new_env["SERVER_PORT"] = 443 if (match && match[1] == "https") new_env["HTTPS"] = false and new_env["rack.url_scheme"] = "http" and new_env["SERVER_PORT"] = 80 if (match && match[1] == "http") end if env["X-FORWARDED-PROTO"] new_env["HTTPS"] = true and new_env["rack.url_scheme"] = "https" and new_env["SERVER_PORT"] = 443 if env["X-FORWARDED-PROTO"].split(',')[0] == "https" new_env["HTTPS"] = false and new_env["rack.url_scheme"] = "http" and new_env["SERVER_PORT"] = 80 if env["X-FORWARDED-PROTO"].split(',')[0] == "http" end if @options[:protocol] new_env["HTTPS"] = true and new_env["rack.url_scheme"] = "https" and new_env["SERVER_PORT"] = 443 if @options[:protocol] == "https" new_env["HTTPS"] = false and new_env["rack.url_scheme"] = "http" and new_env["SERVER_PORT"] = 80 if @options[:protocol] == "http" end url = Rack::Request.new(new_env).url prerender_url = get_prerender_service_url() forward_slash = prerender_url[-1, 1] == '/' ? '' : '/' "#{prerender_url}#{forward_slash}#{url}" end
build_rack_response_from_prerender(prerendered_response)
click to toggle source
# File lib/prerender_rails.rb, line 243 def build_rack_response_from_prerender(prerendered_response) response = Rack::Response.new(prerendered_response.body, prerendered_response.code, prerendered_response) @options[:build_rack_response_from_prerender].call(response, prerendered_response) if @options[:build_rack_response_from_prerender] response end
call(env)
click to toggle source
# File lib/prerender_rails.rb, line 106 def call(env) if should_show_prerendered_page(env) cached_response = before_render(env) if cached_response return cached_response.finish end prerendered_response = get_prerendered_page_response(env) if prerendered_response response = build_rack_response_from_prerender(prerendered_response) after_render(env, prerendered_response) return response.finish end end @app.call(env) end
get_prerender_service_url()
click to toggle source
# File lib/prerender_rails.rb, line 238 def get_prerender_service_url @options[:prerender_service_url] || ENV['PRERENDER_SERVICE_URL'] || 'http://service.prerender.io/' end
get_prerendered_page_response(env)
click to toggle source
# File lib/prerender_rails.rb, line 174 def get_prerendered_page_response(env) begin url = URI.parse(build_api_url(env)) headers = { 'User-Agent' => env['HTTP_USER_AGENT'], 'Accept-Encoding' => 'gzip' } headers['X-Prerender-Token'] = ENV['PRERENDER_TOKEN'] if ENV['PRERENDER_TOKEN'] headers['X-Prerender-Token'] = @options[:prerender_token] if @options[:prerender_token] req = Net::HTTP::Get.new(url.request_uri, headers) req.basic_auth(ENV['PRERENDER_USERNAME'], ENV['PRERENDER_PASSWORD']) if @options[:basic_auth] http = Net::HTTP.new(url.host, url.port) http.use_ssl = true if url.scheme == 'https' response = http.request(req) if response['Content-Encoding'] == 'gzip' response.body = ActiveSupport::Gzip.decompress(response.body) response['Content-Length'] = response.body.length response.delete('Content-Encoding') end hop_by_hop_headers = @hop_by_hop_headers connection = response['Connection'] if connection connection_hop_by_hop_headers = connection.split(','). map(&:strip). map(&:downcase). difference(@connection_header_values) hop_by_hop_headers = connection_hop_by_hop_headers. concat(hop_by_hop_headers) end hop_by_hop_headers.each { |h| response.delete(h) } response rescue nil end end
should_show_prerendered_page(env)
click to toggle source
# File lib/prerender_rails.rb, line 128 def should_show_prerendered_page(env) user_agent = env['HTTP_USER_AGENT'] buffer_agent = env['HTTP_X_BUFFERBOT'] prerender_agent = env['HTTP_X_PRERENDER'] is_requesting_prerendered_page = false return false if !user_agent return false if env['REQUEST_METHOD'] != 'GET' request = Rack::Request.new(env) is_requesting_prerendered_page = true if Rack::Utils.parse_query(request.query_string).has_key?('_escaped_fragment_') #if it is a bot...show prerendered page is_requesting_prerendered_page = true if @crawler_user_agents.any? { |crawler_user_agent| user_agent.downcase.include?(crawler_user_agent.downcase) } #if it is BufferBot...show prerendered page is_requesting_prerendered_page = true if buffer_agent #if it is Prerender...don't show prerendered page is_requesting_prerendered_page = false if prerender_agent #if it is a bot and is requesting a resource...dont prerender return false if @extensions_to_ignore.any? { |extension| request.fullpath.include? extension } #if it is a bot and not requesting a resource and is not whitelisted...dont prerender return false if @options[:whitelist].is_a?(Array) && @options[:whitelist].all? { |whitelisted| !Regexp.new(whitelisted).match(request.fullpath) } #if it is a bot and not requesting a resource and is blacklisted(url or referer)...dont prerender if @options[:blacklist].is_a?(Array) && @options[:blacklist].any? { |blacklisted| blacklistedUrl = false blacklistedReferer = false regex = Regexp.new(blacklisted) blacklistedUrl = !!regex.match(request.fullpath) blacklistedReferer = !!regex.match(request.referer) if request.referer blacklistedUrl || blacklistedReferer } return false end return is_requesting_prerendered_page end