telvue-prerender-rails 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0171b8209477afcf52a4d850b8fc1d5f78543550532a0f3e1214a77ead6b9074
4
+ data.tar.gz: 599fd645634d6813200041a6048aec9d1055f0a0efe0e401d053bd210bb9acea
5
+ SHA512:
6
+ metadata.gz: e775e031b72d27cc405466e786bb85634feb8a31c8c95150e0255d5492300cff8df9ca37ad576a7895138cdcac854b4f8a5a7516f0baa966f1a4cbc74b42ca9d
7
+ data.tar.gz: 0120664a13c7c77b05858f83ad4473396af39edff61ff348c190dcb50339295bf530db9f944ba26d3f60a5414dd430b75724c19e4f77fa561082851c2ed721f2
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in prerender_rails.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Todd Hooper
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,176 @@
1
+ Prerender Rails [![Build Status](https://travis-ci.org/prerender/prerender_rails.png)](https://travis-ci.org/prerender/prerender_rails) [![Gem Version](https://badge.fury.io/rb/prerender_rails.png)](http://badge.fury.io/rb/prerender_rails)
2
+ ===========================
3
+
4
+ Google, Facebook, Twitter, Yahoo, and Bing are constantly trying to view your website... but they don't execute javascript. That's why we built Prerender. Prerender is perfect for AngularJS SEO, BackboneJS SEO, EmberJS SEO, and any other javascript framework.
5
+
6
+ This middleware intercepts requests to your Rails website from crawlers, and then makes a call to the (external) Prerender Service to get the static HTML instead of the javascript for that page.
7
+
8
+ Prerender adheres to google's `_escaped_fragment_` proposal, which we recommend you use. It's easy:
9
+ - Just add <meta name="fragment" content="!"> to the <head> of all of your pages
10
+ - If you use hash urls (#), change them to the hash-bang (#!)
11
+ - That's it! Perfect SEO on javascript pages.
12
+
13
+ `Note` Make sure you have more than one webserver thread/process running because the prerender service will make a request to your server to render the HTML.
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'prerender_rails'
18
+
19
+ And in `config/environment/production.rb`, add this line:
20
+
21
+ ```ruby
22
+ config.middleware.use Rack::Prerender
23
+ ```
24
+
25
+ or if you have an account on [prerender.io](http://prerender.io) and want to use your token:
26
+
27
+ ```ruby
28
+ config.middleware.use Rack::Prerender, prerender_token: 'YOUR_TOKEN'
29
+ ```
30
+
31
+ `Note` If you're testing locally, you'll need to run the [prerender server](https://github.com/prerender/prerender) locally so that it has access to your server.
32
+
33
+ ## Testing
34
+
35
+ When testing make sure you're not using a single threaded application server like default WEBrick one, use Puma or Unicorn.
36
+
37
+ If your URLs use a hash-bang:
38
+
39
+ If you want to see `http://localhost:5000/#!/profiles/1234`
40
+ Then go to `http://localhost:5000/?_escaped_fragment_=/profiles/1234`
41
+
42
+ If your URLs use push-state:
43
+
44
+ If you want to see `http://localhost:5000/profiles/1234`
45
+ Then go to `http://localhost:5000/profiles/1234?_escaped_fragment_=`
46
+
47
+ ## How it works
48
+ 1. The middleware checks to make sure we should show a prerendered page
49
+ 1. The middleware checks if the request is from a crawler (`_escaped_fragment_` or agent string)
50
+ 2. The middleware checks to make sure we aren't requesting a resource (js, css, etc...)
51
+ 3. (optional) The middleware checks to make sure the url is in the whitelist
52
+ 4. (optional) The middleware checks to make sure the url isn't in the blacklist
53
+ 2. The middleware makes a `GET` request to the [prerender service](https://github.com/prerender/prerender)(phantomjs server) for the page's prerendered HTML
54
+ 3. Return that HTML to the crawler
55
+
56
+ # Customization
57
+
58
+ ### Whitelist
59
+
60
+ Whitelist a single url path or multiple url paths. Compares using regex, so be specific when possible. If a whitelist is supplied, only url's containing a whitelist path will be prerendered.
61
+ ```ruby
62
+ config.middleware.use Rack::Prerender, whitelist: '^/search'
63
+ ```
64
+ ```ruby
65
+ config.middleware.use Rack::Prerender, whitelist: ['/search', '/users/.*/profile']
66
+ ```
67
+
68
+ ### Blacklist
69
+
70
+ Blacklist a single url path or multiple url paths. Compares using regex, so be specific when possible. If a blacklist is supplied, all url's will be prerendered except ones containing a blacklist path.
71
+ ```ruby
72
+ config.middleware.use Rack::Prerender, blacklist: '^/search'
73
+ ```
74
+ ```ruby
75
+ config.middleware.use Rack::Prerender, blacklist: ['/search', '/users/.*/profile']
76
+ ```
77
+
78
+ ### before_render
79
+
80
+ This method is intended to be used for caching, but could be used to save analytics or anything else you need to do for each crawler request. If you return a string from before_render, the middleware will serve that to the crawler instead of making a request to the prerender service.
81
+ ```ruby
82
+ config.middleware.use Rack::Prerender,
83
+ before_render: (Proc.new do |env|
84
+ # do whatever you need to do.
85
+ end)
86
+ ```
87
+
88
+ ### after_render
89
+
90
+ This method is intended to be used for caching, but could be used to save analytics or anything else you need to do for each crawler request. This method is a noop and is called after the prerender service returns HTML.
91
+ ```ruby
92
+ config.middleware.use Rack::Prerender,
93
+ after_render: (Proc.new do |env, response|
94
+ # do whatever you need to do.
95
+ end)
96
+ ```
97
+
98
+ ### build_rack_response_from_prerender
99
+
100
+ This method is intended to be used to modify the response before it is sent to the crawler. Use this method to add/remove response headers, or do anything else before the request is sent.
101
+ ```ruby
102
+ config.middleware.use Rack::Prerender,
103
+ build_rack_response_from_prerender: (Proc.new do |response, prerender_response|
104
+ # response is already populated with the prerender status code, html, and headers
105
+ # prerender_response is the response that came back from the prerender service
106
+ end)
107
+ ```
108
+
109
+ ### protocol
110
+
111
+ Option to hard-set the protocol for Prerender accessing your site instead of the middleware figuring out the protocol based on the request.
112
+ ```ruby
113
+ config.middleware.use Rack::Prerender, protocol: 'https'
114
+ ```
115
+
116
+ ## Caching
117
+
118
+ This rails middleware is ready to be used with [redis](http://redis.io/) or [memcached](http://memcached.org/) to return prerendered pages in milliseconds.
119
+
120
+ When setting up the middleware in `config/environment/production.rb`, you can add a `before_render` method and `after_render` method for caching.
121
+
122
+ Here's an example testing a local redis cache:
123
+
124
+ _Put this in `config/environment/development.rb`, and add `gem 'redis'` to your Gemfile._
125
+
126
+ ```ruby
127
+ require 'redis'
128
+ @redis = Redis.new
129
+ config.middleware.use Rack::Prerender,
130
+ before_render: (Proc.new do |env|
131
+ @redis.get(Rack::Request.new(env).url)
132
+ end),
133
+ after_render: (Proc.new do |env, response|
134
+ @redis.set(Rack::Request.new(env).url, response.body)
135
+ end)
136
+ ```
137
+
138
+ ## Using your own prerender service
139
+
140
+ We host a Prerender server at [prerender.io](http://prerender.io) so that you can work on more important things, but if you've deployed the prerender service on your own... set the `PRERENDER_SERVICE_URL` environment variable so that this middleware points there instead. Otherwise, it will default to the service already deployed by [prerender.io](http://prerender.io).
141
+
142
+ $ export PRERENDER_SERVICE_URL=<new url>
143
+
144
+ Or on heroku:
145
+
146
+ $ heroku config:add PRERENDER_SERVICE_URL=<new url>
147
+
148
+ As an alternative, you can pass `prerender_service_url` in the options object during initialization of the middleware
149
+
150
+ ``` ruby
151
+ config.middleware.use Rack::Prerender, prerender_service_url: '<new url>'
152
+ ```
153
+
154
+ ## License
155
+
156
+ The MIT License (MIT)
157
+
158
+ Copyright (c) 2013 Todd Hooper &lt;todd@prerender.io&gt;
159
+
160
+ Permission is hereby granted, free of charge, to any person obtaining a copy
161
+ of this software and associated documentation files (the "Software"), to deal
162
+ in the Software without restriction, including without limitation the rights
163
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
164
+ copies of the Software, and to permit persons to whom the Software is
165
+ furnished to do so, subject to the following conditions:
166
+
167
+ The above copyright notice and this permission notice shall be included in
168
+ all copies or substantial portions of the Software.
169
+
170
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
172
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
173
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
174
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
175
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
176
+ THE SOFTWARE.
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'lib/prerender_rails'
8
+ t.test_files = FileList['test/lib/prerender_rails.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,254 @@
1
+ module Rack
2
+ class Prerender
3
+ require 'net/http'
4
+ require 'active_support'
5
+
6
+ def initialize(app, options={})
7
+ @crawler_user_agents = [
8
+ 'googlebot',
9
+ 'yahoo',
10
+ 'bingbot',
11
+ 'baiduspider',
12
+ 'facebookexternalhit',
13
+ 'twitterbot',
14
+ 'rogerbot',
15
+ 'linkedinbot',
16
+ 'embedly',
17
+ 'bufferbot',
18
+ 'quora link preview',
19
+ 'showyoubot',
20
+ 'outbrain',
21
+ 'pinterest/0.',
22
+ 'developers.google.com/+/web/snippet',
23
+ 'www.google.com/webmasters/tools/richsnippets',
24
+ 'slackbot',
25
+ 'vkShare',
26
+ 'W3C_Validator',
27
+ 'redditbot',
28
+ 'Applebot',
29
+ 'WhatsApp',
30
+ 'flipboard',
31
+ 'tumblr',
32
+ 'bitlybot',
33
+ 'SkypeUriPreview',
34
+ 'nuzzel',
35
+ 'Discordbot',
36
+ 'Google Page Speed',
37
+ 'Qwantify',
38
+ 'Chrome-Lighthouse'
39
+ ]
40
+
41
+ @extensions_to_ignore = [
42
+ '.js',
43
+ '.css',
44
+ '.xml',
45
+ '.less',
46
+ '.png',
47
+ '.jpg',
48
+ '.jpeg',
49
+ '.gif',
50
+ '.pdf',
51
+ '.doc',
52
+ '.txt',
53
+ '.ico',
54
+ '.rss',
55
+ '.zip',
56
+ '.mp3',
57
+ '.rar',
58
+ '.exe',
59
+ '.wmv',
60
+ '.doc',
61
+ '.avi',
62
+ '.ppt',
63
+ '.mpg',
64
+ '.mpeg',
65
+ '.tif',
66
+ '.wav',
67
+ '.mov',
68
+ '.psd',
69
+ '.ai',
70
+ '.xls',
71
+ '.mp4',
72
+ '.m4a',
73
+ '.swf',
74
+ '.dat',
75
+ '.dmg',
76
+ '.iso',
77
+ '.flv',
78
+ '.m4v',
79
+ '.torrent'
80
+ ]
81
+
82
+ @options = options
83
+ @options[:whitelist] = [@options[:whitelist]] if @options[:whitelist].is_a? String
84
+ @options[:blacklist] = [@options[:blacklist]] if @options[:blacklist].is_a? String
85
+ @extensions_to_ignore = @options[:extensions_to_ignore] if @options[:extensions_to_ignore]
86
+ @crawler_user_agents = @options[:crawler_user_agents] if @options[:crawler_user_agents]
87
+ @app = app
88
+ end
89
+
90
+
91
+ def call(env)
92
+ if should_show_prerendered_page(env)
93
+
94
+ cached_response = before_render(env)
95
+
96
+ if cached_response
97
+ return cached_response.finish
98
+ end
99
+
100
+ prerendered_response = get_prerendered_page_response(env)
101
+
102
+ if prerendered_response
103
+ response = build_rack_response_from_prerender(prerendered_response)
104
+ after_render(env, prerendered_response)
105
+ return response.finish
106
+ end
107
+ end
108
+
109
+ @app.call(env)
110
+ end
111
+
112
+
113
+ def should_show_prerendered_page(env)
114
+ user_agent = env['HTTP_USER_AGENT']
115
+ buffer_agent = env['HTTP_X_BUFFERBOT']
116
+ prerender_agent = env['HTTP_X_PRERENDER']
117
+ is_requesting_prerendered_page = false
118
+
119
+ return false if !user_agent
120
+ return false if env['REQUEST_METHOD'] != 'GET'
121
+
122
+ request = Rack::Request.new(env)
123
+
124
+ is_requesting_prerendered_page = true if Rack::Utils.parse_query(request.query_string).has_key?('_escaped_fragment_')
125
+
126
+ #if it is a bot...show prerendered page
127
+ is_requesting_prerendered_page = true if @crawler_user_agents.any? { |crawler_user_agent| user_agent.downcase.include?(crawler_user_agent.downcase) }
128
+
129
+ #if it is BufferBot...show prerendered page
130
+ is_requesting_prerendered_page = true if buffer_agent
131
+
132
+ #if it is Prerender...don't show prerendered page
133
+ is_requesting_prerendered_page = false if prerender_agent
134
+
135
+ #if it is a bot and is requesting a resource...dont prerender
136
+ return false if @extensions_to_ignore.any? { |extension| request.fullpath.include? extension }
137
+
138
+ #if it is a bot and not requesting a resource and is not whitelisted...dont prerender
139
+ return false if @options[:whitelist].is_a?(Array) && @options[:whitelist].all? { |whitelisted| !Regexp.new(whitelisted).match(request.fullpath) }
140
+
141
+ #if it is a bot and not requesting a resource and is blacklisted(url or referer)...dont prerender
142
+ if @options[:blacklist].is_a?(Array) && @options[:blacklist].any? { |blacklisted|
143
+ blacklistedUrl = false
144
+ blacklistedReferer = false
145
+ regex = Regexp.new(blacklisted)
146
+
147
+ blacklistedUrl = !!regex.match(request.fullpath)
148
+ blacklistedReferer = !!regex.match(request.referer) if request.referer
149
+
150
+ blacklistedUrl || blacklistedReferer
151
+ }
152
+ return false
153
+ end
154
+
155
+ return is_requesting_prerendered_page
156
+ end
157
+
158
+
159
+ def get_prerendered_page_response(env)
160
+ begin
161
+ url = URI.parse(build_api_url(env))
162
+ headers = {
163
+ 'User-Agent' => env['HTTP_USER_AGENT'],
164
+ 'Accept-Encoding' => 'gzip'
165
+ }
166
+ headers['X-Prerender-Token'] = ENV['PRERENDER_TOKEN'] if ENV['PRERENDER_TOKEN']
167
+ headers['X-Prerender-Token'] = @options[:prerender_token] if @options[:prerender_token]
168
+ req = Net::HTTP::Get.new(url.request_uri, headers)
169
+ req.basic_auth(ENV['PRERENDER_USERNAME'], ENV['PRERENDER_PASSWORD']) if @options[:basic_auth]
170
+ http = Net::HTTP.new(url.host, url.port)
171
+ http.use_ssl = true if url.scheme == 'https'
172
+ response = http.request(req)
173
+ if response['Content-Encoding'] == 'gzip'
174
+ response.body = ActiveSupport::Gzip.decompress(response.body)
175
+ response['Content-Length'] = response.body.length
176
+ response.delete('Content-Encoding')
177
+ end
178
+ response
179
+ rescue
180
+ nil
181
+ end
182
+ end
183
+
184
+
185
+ def build_api_url(env)
186
+ new_env = env
187
+ if env["CF-VISITOR"]
188
+ match = /"scheme":"(http|https)"/.match(env['CF-VISITOR'])
189
+ new_env["HTTPS"] = true and new_env["rack.url_scheme"] = "https" and new_env["SERVER_PORT"] = 443 if (match && match[1] == "https")
190
+ new_env["HTTPS"] = false and new_env["rack.url_scheme"] = "http" and new_env["SERVER_PORT"] = 80 if (match && match[1] == "http")
191
+ end
192
+
193
+ if env["X-FORWARDED-PROTO"]
194
+ 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"
195
+ 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"
196
+ end
197
+
198
+ if @options[:protocol]
199
+ new_env["HTTPS"] = true and new_env["rack.url_scheme"] = "https" and new_env["SERVER_PORT"] = 443 if @options[:protocol] == "https"
200
+ new_env["HTTPS"] = false and new_env["rack.url_scheme"] = "http" and new_env["SERVER_PORT"] = 80 if @options[:protocol] == "http"
201
+ end
202
+
203
+ url = Rack::Request.new(new_env).url
204
+ prerender_url = get_prerender_service_url()
205
+ forward_slash = prerender_url[-1, 1] == '/' ? '' : '/'
206
+
207
+
208
+ if env['HTTP_USER_AGENT'].include?('facebookexternalhit')
209
+ if url.include?('?')
210
+ url = "#{url}&ua=fb_crawler"
211
+ else
212
+ url = "#{url}?ua=fb_crawler"
213
+ end
214
+ end
215
+
216
+
217
+ "#{prerender_url}#{forward_slash}#{url}"
218
+ end
219
+
220
+
221
+ def get_prerender_service_url
222
+ @options[:prerender_service_url] || ENV['PRERENDER_SERVICE_URL'] || 'http://service.prerender.io/'
223
+ end
224
+
225
+
226
+ def build_rack_response_from_prerender(prerendered_response)
227
+ response = Rack::Response.new(prerendered_response.body, prerendered_response.code, prerendered_response.header)
228
+
229
+ @options[:build_rack_response_from_prerender].call(response, prerendered_response) if @options[:build_rack_response_from_prerender]
230
+
231
+ response
232
+ end
233
+
234
+ def before_render(env)
235
+ return nil unless @options[:before_render]
236
+
237
+ cached_render = @options[:before_render].call(env)
238
+
239
+ if cached_render && cached_render.is_a?(String)
240
+ Rack::Response.new(cached_render, 200, { 'Content-Type' => 'text/html; charset=utf-8' })
241
+ elsif cached_render && cached_render.is_a?(Rack::Response)
242
+ cached_render
243
+ else
244
+ nil
245
+ end
246
+ end
247
+
248
+
249
+ def after_render(env, response)
250
+ return true unless @options[:after_render]
251
+ @options[:after_render].call(env, response)
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "telvue-prerender-rails"
5
+ spec.version = "1.6.2"
6
+ spec.authors = ["Todd Hooper", "Ben Liu"]
7
+ spec.email = ["todd@prerender.io", "bliu@telvue.com"]
8
+ spec.description = %q{Rails middleware to prerender your javascript heavy pages on the fly by a phantomjs service}
9
+ spec.summary = %q{Prerender your backbone/angular/javascript rendered application on the fly when search engines crawl}
10
+ spec.homepage = "https://github.com/prerender/prerender_rails"
11
+ spec.license = "MIT"
12
+
13
+ spec.files = `git ls-files`.split($/)
14
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_dependency 'rack', '>= 0'
19
+ spec.add_dependency 'activesupport', '>= 0'
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "webmock"
24
+ end
@@ -0,0 +1,224 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe Rack::Prerender do
4
+
5
+ bot = 'Baiduspider+(+http://www.baidu.com/search/spider.htm)'
6
+ user = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36'
7
+
8
+ before :each do
9
+ @app = lambda do |params|
10
+ [200, {}, ""]
11
+ end
12
+
13
+ @prerender = Rack::Prerender.new(@app)
14
+ end
15
+
16
+
17
+ it "should return a prerendered response for a crawler with the returned status code and headers" do
18
+ request = Rack::MockRequest.env_for "/", "HTTP_USER_AGENT" => bot
19
+ stub_request(:get, @prerender.build_api_url(request)).with(:headers => { 'User-Agent' => bot }).to_return(:body => "<html></html>", :status => 301, :headers => { 'Location' => 'http://google.com'})
20
+ response = Rack::Prerender.new(@app).call(request)
21
+
22
+ assert_equal response[2].body, ["<html></html>"]
23
+ assert_equal response[2].status, 301
24
+ assert_equal( { 'location' => 'http://google.com', 'Content-Length' => '13'}, response[2].headers )
25
+ end
26
+
27
+
28
+ it "should return a prerendered reponse if user is a bot by checking for _escaped_fragment_" do
29
+ request = Rack::MockRequest.env_for "/path?_escaped_fragment_=", "HTTP_USER_AGENT" => user
30
+ stub_request(:get, @prerender.build_api_url(request)).with(:headers => { 'User-Agent' => user }).to_return(:body => "<html></html>")
31
+ response = Rack::Prerender.new(@app).call(request)
32
+
33
+ assert_equal ["<html></html>"], response[2].body
34
+ end
35
+
36
+
37
+ it "should continue to app routes if the url is a bad url with _escaped_fragment_" do
38
+ request = Rack::MockRequest.env_for "/path?query=string?_escaped_fragment_=", "HTTP_USER_AGENT" => user
39
+ response = Rack::Prerender.new(@app).call(request)
40
+
41
+ assert_equal "", response[2]
42
+ end
43
+
44
+
45
+ it "should continue to app routes if the request is not a GET" do
46
+ request = Rack::MockRequest.env_for "/path?_escaped_fragment_=", { "HTTP_USER_AGENT" => user, "REQUEST_METHOD" => "POST" }
47
+ response = Rack::Prerender.new(@app).call(request)
48
+
49
+ assert_equal "", response[2]
50
+ end
51
+
52
+
53
+ it "should continue to app routes if user is not a bot by checking agent string" do
54
+ request = Rack::MockRequest.env_for "/", "HTTP_USER_AGENT" => user
55
+ response = Rack::Prerender.new(@app).call(request)
56
+
57
+ assert_equal "", response[2]
58
+ end
59
+
60
+
61
+ it "should continue to app routes if contains X-Prerender header" do
62
+ request = Rack::MockRequest.env_for "/path?_escaped_fragment_=", "HTTP_USER_AGENT" => user, "HTTP_X_PRERENDER" => "1"
63
+ response = Rack::Prerender.new(@app).call(request)
64
+
65
+ assert_equal "", response[2]
66
+ end
67
+
68
+
69
+ it "should continue to app routes if user is a bot, but the bot is requesting a resource file" do
70
+ request = Rack::MockRequest.env_for "/main.js?anyQueryParam=true", "HTTP_USER_AGENT" => bot
71
+ response = Rack::Prerender.new(@app).call(request)
72
+
73
+ assert_equal "", response[2]
74
+ end
75
+
76
+
77
+ it "should continue to app routes if the url is not part of the regex specific whitelist" do
78
+ request = Rack::MockRequest.env_for "/saved/search/blah?_escaped_fragment_=", "HTTP_USER_AGENT" => bot
79
+ response = Rack::Prerender.new(@app, whitelist: ['^/search', '/help']).call(request)
80
+
81
+ assert_equal "", response[2]
82
+ end
83
+
84
+
85
+ it "should set use_ssl to true for https prerender_service_url" do
86
+ @prerender = Rack::Prerender.new(@app, prerender_service_url: 'https://service.prerender.io/')
87
+
88
+ request = Rack::MockRequest.env_for "/search/things/123/page?_escaped_fragment_=", "HTTP_USER_AGENT" => bot
89
+ stub_request(:get, @prerender.build_api_url(request)).to_return(:body => "<html></html>")
90
+ response = @prerender.call(request)
91
+
92
+ assert_equal ["<html></html>"], response[2].body
93
+ end
94
+
95
+
96
+ it "should return a prerendered response if the url is part of the regex specific whitelist" do
97
+ request = Rack::MockRequest.env_for "/search/things/123/page?_escaped_fragment_=", "HTTP_USER_AGENT" => bot
98
+ stub_request(:get, @prerender.build_api_url(request)).to_return(:body => "<html></html>")
99
+ response = Rack::Prerender.new(@app, whitelist: ['^/search.*page', '/help']).call(request)
100
+
101
+ assert_equal ["<html></html>"], response[2].body
102
+ end
103
+
104
+
105
+ it "should continue to app routes if the url is part of the regex specific blacklist" do
106
+ request = Rack::MockRequest.env_for "/search/things/123/page", "HTTP_USER_AGENT" => bot
107
+ response = Rack::Prerender.new(@app, blacklist: ['^/search', '/help']).call(request)
108
+
109
+ assert_equal "", response[2]
110
+ end
111
+
112
+ it "should continue to app routes if the hashbang url is part of the regex specific blacklist" do
113
+ request = Rack::MockRequest.env_for "?_escaped_fragment_=/search/things/123/page", "HTTP_USER_AGENT" => bot
114
+ response = Rack::Prerender.new(@app, blacklist: ['/search', '/help']).call(request)
115
+
116
+ assert_equal "", response[2]
117
+ end
118
+
119
+ it "should return a prerendered response if the url is not part of the regex specific blacklist" do
120
+ request = Rack::MockRequest.env_for "/profile/search/blah", "HTTP_USER_AGENT" => bot
121
+ stub_request(:get, @prerender.build_api_url(request)).to_return(:body => "<html></html>")
122
+ response = Rack::Prerender.new(@app, blacklist: ['^/search', '/help']).call(request)
123
+
124
+ assert_equal ["<html></html>"], response[2].body
125
+ end
126
+
127
+
128
+ it "should continue to app routes if the referer is part of the regex specific blacklist" do
129
+ request = Rack::MockRequest.env_for "/api/results", "HTTP_USER_AGENT" => bot, "HTTP_REFERER" => '/search'
130
+ response = Rack::Prerender.new(@app, blacklist: ['^/search', '/help']).call(request)
131
+
132
+ assert_equal "", response[2]
133
+ end
134
+
135
+
136
+ it "should return a prerendered response if the referer is not part of the regex specific blacklist" do
137
+ request = Rack::MockRequest.env_for "/api/results", "HTTP_USER_AGENT" => bot, "HTTP_REFERER" => '/profile/search'
138
+ stub_request(:get, @prerender.build_api_url(request)).to_return(:body => "<html></html>")
139
+ response = Rack::Prerender.new(@app, blacklist: ['^/search', '/help']).call(request)
140
+
141
+ assert_equal ["<html></html>"], response[2].body
142
+ end
143
+
144
+
145
+ it "should return a prerendered response if a string is returned from before_render" do
146
+ request = Rack::MockRequest.env_for "/", "HTTP_USER_AGENT" => bot
147
+ response = Rack::Prerender.new(@app, before_render: Proc.new do |env| '<html>cached</html>' end).call(request)
148
+
149
+ assert_equal ["<html>cached</html>"], response[2].body
150
+ end
151
+
152
+
153
+ it "should return a prerendered response if a response is returned from before_render" do
154
+ request = Rack::MockRequest.env_for "/", "HTTP_USER_AGENT" => bot
155
+ response = Rack::Prerender.new(@app, before_render: Proc.new do |env| Rack::Response.new('<html>cached2</html>', 200, { 'test' => 'test2Header'}) end).call(request)
156
+
157
+ assert_equal ["<html>cached2</html>"], response[2].body
158
+ assert_equal response[2].status, 200
159
+ assert_equal( { 'test' => 'test2Header', "Content-Length"=>"20"}, response[2].headers )
160
+ end
161
+
162
+
163
+ describe '#buildApiUrl' do
164
+ it "should build the correct api url with the default url" do
165
+ request = Rack::MockRequest.env_for "https://google.com/search?q=javascript"
166
+ ENV['PRERENDER_SERVICE_URL'] = nil
167
+ assert_equal 'http://service.prerender.io/https://google.com/search?q=javascript', @prerender.build_api_url(request)
168
+ end
169
+
170
+
171
+ it "should build the correct api url with an environment variable url" do
172
+ ENV['PRERENDER_SERVICE_URL'] = 'http://prerenderurl.com'
173
+ request = Rack::MockRequest.env_for "https://google.com/search?q=javascript"
174
+ assert_equal 'http://prerenderurl.com/https://google.com/search?q=javascript', @prerender.build_api_url(request)
175
+ ENV['PRERENDER_SERVICE_URL'] = nil
176
+ end
177
+
178
+
179
+ it "should build the correct api url with an initialization variable url" do
180
+ @prerender = Rack::Prerender.new(@app, prerender_service_url: 'http://prerenderurl.com')
181
+ request = Rack::MockRequest.env_for "https://google.com/search?q=javascript"
182
+ assert_equal 'http://prerenderurl.com/https://google.com/search?q=javascript', @prerender.build_api_url(request)
183
+ end
184
+
185
+
186
+ it "should build the correct https api url with an initialization variable url" do
187
+ @prerender = Rack::Prerender.new(@app, prerender_service_url: 'https://prerenderurl.com')
188
+ request = Rack::MockRequest.env_for "https://google.com/search?q=javascript"
189
+ assert_equal 'https://prerenderurl.com/https://google.com/search?q=javascript', @prerender.build_api_url(request)
190
+ end
191
+
192
+
193
+ # Check CF-Visitor header in order to Work behind CloudFlare with Flexible SSL (https://support.cloudflare.com/hc/en-us/articles/200170536)
194
+ it "should build the correct api url for the Cloudflare Flexible SSL support" do
195
+ request = Rack::MockRequest.env_for "http://google.com/search?q=javascript", { 'CF-VISITOR' => '"scheme":"https"'}
196
+ ENV['PRERENDER_SERVICE_URL'] = nil
197
+ assert_equal 'http://service.prerender.io/https://google.com/search?q=javascript', @prerender.build_api_url(request)
198
+ end
199
+
200
+
201
+ # Check X-Forwarded-Proto because Heroku SSL Support terminates at the load balancer
202
+ it "should build the correct api url for the Heroku SSL Addon support with single value" do
203
+ request = Rack::MockRequest.env_for "http://google.com/search?q=javascript", { 'X-FORWARDED-PROTO' => 'https'}
204
+ ENV['PRERENDER_SERVICE_URL'] = nil
205
+ assert_equal 'http://service.prerender.io/https://google.com/search?q=javascript', @prerender.build_api_url(request)
206
+ end
207
+
208
+
209
+ # Check X-Forwarded-Proto because Heroku SSL Support terminates at the load balancer
210
+ it "should build the correct api url for the Heroku SSL Addon support with double value" do
211
+ request = Rack::MockRequest.env_for "http://google.com/search?q=javascript", { 'X-FORWARDED-PROTO' => 'https,http'}
212
+ ENV['PRERENDER_SERVICE_URL'] = nil
213
+ assert_equal 'http://service.prerender.io/https://google.com/search?q=javascript', @prerender.build_api_url(request)
214
+ end
215
+
216
+ it "should build the correct api url for the protocol option" do
217
+ @prerender = Rack::Prerender.new(@app, protocol: 'https')
218
+ request = Rack::MockRequest.env_for "http://google.com/search?q=javascript"
219
+ ENV['PRERENDER_SERVICE_URL'] = nil
220
+ assert_equal 'http://service.prerender.io/https://google.com/search?q=javascript', @prerender.build_api_url(request)
221
+ end
222
+ end
223
+
224
+ end
@@ -0,0 +1,4 @@
1
+ require 'minitest/autorun'
2
+ require 'webmock/minitest'
3
+ require 'rack'
4
+ require File.expand_path('../../lib/prerender_rails.rb', __FILE__)
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: telvue-prerender-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.6.2
5
+ platform: ruby
6
+ authors:
7
+ - Todd Hooper
8
+ - Ben Liu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-10-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: activesupport
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.3'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.3'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: webmock
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ description: Rails middleware to prerender your javascript heavy pages on the fly
85
+ by a phantomjs service
86
+ email:
87
+ - todd@prerender.io
88
+ - bliu@telvue.com
89
+ executables: []
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - ".gitignore"
94
+ - ".travis.yml"
95
+ - Gemfile
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - lib/prerender_rails.rb
100
+ - prerender_rails.gemspec
101
+ - test/lib/prerender_rails.rb
102
+ - test/test_helper.rb
103
+ homepage: https://github.com/prerender/prerender_rails
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubygems_version: 3.0.6
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Prerender your backbone/angular/javascript rendered application on the fly
126
+ when search engines crawl
127
+ test_files:
128
+ - test/lib/prerender_rails.rb
129
+ - test/test_helper.rb