prerendercloud 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5682cc335eaeab97282c40c13b90856a311a2067
4
+ data.tar.gz: 8ad8486d91b632356e9cddc5b10a6777f4c0a65e
5
+ SHA512:
6
+ metadata.gz: 70531d251e9001ea5d585fa74a54b64980c03cdd2e4b9cc63eb2c89efc8852b6e4d075483b720bab310b3808ebcf59c082a9f3da37248b3b802da20044b8fb86
7
+ data.tar.gz: 3c276ac603ba71eb5777251f59ea9214d91ff9b6c1bd574c0d860719a566923117a6157a9e35989d1176e6dcc8b0a59427938c40d938e64edd260cbbf4e1f36f
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in prerender_rails.gemspec
4
+ gemspec
5
+
data/Gemfile.lock ADDED
@@ -0,0 +1,45 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ prerendercloud (0.1.0)
5
+ activesupport
6
+ rack
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (4.2.8)
12
+ i18n (~> 0.7)
13
+ minitest (~> 5.1)
14
+ thread_safe (~> 0.3, >= 0.3.4)
15
+ tzinfo (~> 1.1)
16
+ addressable (2.5.1)
17
+ public_suffix (~> 2.0, >= 2.0.2)
18
+ crack (0.4.3)
19
+ safe_yaml (~> 1.0.0)
20
+ hashdiff (0.3.4)
21
+ i18n (0.8.4)
22
+ minitest (5.10.2)
23
+ public_suffix (2.0.5)
24
+ rack (1.6.8)
25
+ rake (12.0.0)
26
+ safe_yaml (1.0.4)
27
+ thread_safe (0.3.6)
28
+ tzinfo (1.2.3)
29
+ thread_safe (~> 0.1)
30
+ webmock (3.0.1)
31
+ addressable (>= 2.3.6)
32
+ crack (>= 0.3.2)
33
+ hashdiff
34
+
35
+ PLATFORMS
36
+ ruby
37
+
38
+ DEPENDENCIES
39
+ bundler (~> 1.3)
40
+ prerendercloud!
41
+ rake
42
+ webmock
43
+
44
+ BUNDLED WITH
45
+ 1.14.3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 San Francesco
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ ![image](https://cloud.githubusercontent.com/assets/22159102/21554484/9d542f5a-cdc4-11e6-8c4c-7730a9e9e2d1.png)
2
+
3
+ # prerendercloud-ruby
4
+
5
+ Rack middleware for server-side rendering (prerendering) JavaScript web pages/apps (single page apps or SPA) with [https://www.prerender.cloud/](https://www.prerender.cloud/)
6
+
7
+
8
+ ## Rails Usage
9
+
10
+ `Gemfile`
11
+
12
+ ```ruby
13
+ gem 'prerendercloud'
14
+ ```
15
+
16
+ `config/environment/production.rb`
17
+
18
+ ```ruby
19
+
20
+ config.middleware.use Rack::Prerendercloud
21
+ # either hard code your secret token:
22
+ # config.middleware.use Rack::Prerendercloud, prerender_token: 'YOUR_TOKEN'
23
+ # or set the PRERENDER_TOKEN environment variable
24
+
25
+
26
+ ```
data/Rakefile ADDED
@@ -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/prerendercloud'
8
+ t.test_files = FileList['test/lib/prerendercloud.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,253 @@
1
+ module Rack
2
+ class Prerendercloud
3
+ require 'net/http'
4
+ require 'active_support'
5
+
6
+ class << self
7
+
8
+ def header_whitelist
9
+ # preserve (and send to client) these headers from service.prerender.cloud
10
+ # which originally came from the origin server
11
+ return [
12
+ "vary",
13
+ "content-type",
14
+ "cache-control",
15
+ "strict-transport-security",
16
+ "content-security-policy",
17
+ "public-key-pins",
18
+ "x-frame-options",
19
+ "x-xss-protection",
20
+ "x-content-type-options"
21
+ ];
22
+ end
23
+
24
+ def normalize_headers(headers)
25
+ filtered = headers.select { |key, value| header_whitelist.any? { |h| key.to_s.match(/#{h}/i) } }
26
+
27
+ mapped = filtered.map do |k, v|
28
+ [k, if v.is_a? Array then v.join("\n") else v end]
29
+ end
30
+ Utils::HeaderHash.new Hash[mapped]
31
+ end
32
+ end
33
+
34
+ def initialize(app, options={})
35
+ # googlebot, yahoo, and bingbot are not in this list because
36
+ # we support _escaped_fragment_ and want to ensure people aren't
37
+ # penalized for cloaking.
38
+ @crawler_user_agents = [
39
+ 'googlebot',
40
+ 'yahoo',
41
+ 'bingbot',
42
+ 'baiduspider',
43
+ 'facebookexternalhit',
44
+ 'twitterbot',
45
+ 'rogerbot',
46
+ 'linkedinbot',
47
+ 'embedly',
48
+ 'bufferbot',
49
+ 'quora link preview',
50
+ 'showyoubot',
51
+ 'outbrain',
52
+ 'pinterest/0.',
53
+ 'developers.google.com/+/web/snippet',
54
+ 'www.google.com/webmasters/tools/richsnippets',
55
+ 'slackbot',
56
+ 'vkShare',
57
+ 'W3C_Validator',
58
+ 'redditbot',
59
+ 'Applebot',
60
+ 'WhatsApp',
61
+ 'flipboard',
62
+ 'tumblr',
63
+ 'bitlybot',
64
+ 'SkypeUriPreview',
65
+ 'nuzzel',
66
+ 'Discordbot',
67
+ 'Google Page Speed',
68
+ 'Qwantify'
69
+ ]
70
+
71
+ @options = options
72
+ @options[:whitelist] = [@options[:whitelist]] if @options[:whitelist].is_a? String
73
+ @options[:blacklist] = [@options[:blacklist]] if @options[:blacklist].is_a? String
74
+ @crawler_user_agents = @options[:crawler_user_agents] if @options[:crawler_user_agents]
75
+ @app = app
76
+ end
77
+
78
+
79
+ def call(env)
80
+ if should_show_prerendered_page(env)
81
+
82
+ cached_response = before_render(env)
83
+
84
+ if cached_response
85
+ return cached_response.finish
86
+ end
87
+
88
+ prerendered_response = get_prerendered_page_response(env)
89
+
90
+ if prerendered_response
91
+ response = build_rack_response_from_prerender(prerendered_response)
92
+ after_render(env, prerendered_response)
93
+ return response.finish
94
+ end
95
+ end
96
+
97
+ @app.call(env)
98
+ end
99
+
100
+
101
+ def should_show_prerendered_page(env)
102
+ user_agent = env['HTTP_USER_AGENT']
103
+ buffer_agent = env['HTTP_X_BUFFERBOT']
104
+ is_requesting_prerendered_page = false
105
+
106
+ return false if !user_agent
107
+ return false if env['REQUEST_METHOD'] != 'GET'
108
+ return false if user_agent.match(/prerendercloud/i);
109
+
110
+ request = Rack::Request.new(env)
111
+
112
+ return false if !prerenderable_extension(request.fullpath);
113
+
114
+ #if it is a bot...show prerendered page
115
+ if (@options[:bots_only])
116
+ is_requesting_prerendered_page = true if @crawler_user_agents.any? { |crawler_user_agent| user_agent.downcase.include?(crawler_user_agent.downcase) }
117
+
118
+ #if it is BufferBot...show prerendered page
119
+ is_requesting_prerendered_page = true if buffer_agent
120
+ else
121
+ is_requesting_prerendered_page = true
122
+ end
123
+
124
+ is_requesting_prerendered_page = true if Rack::Utils.parse_query(request.query_string).has_key?('_escaped_fragment_')
125
+
126
+ # if whitelist exists and path is not whitelisted...don't prerender
127
+ return false if @options[:whitelist].is_a?(Array) && @options[:whitelist].all? { |whitelisted| !Regexp.new(whitelisted).match(request.fullpath) }
128
+
129
+ # if blacklist exists and path is blacklisted(url or referer)...don't prerender
130
+ if @options[:blacklist].is_a?(Array) && @options[:blacklist].any? { |blacklisted|
131
+ blacklistedUrl = false
132
+ blacklistedReferer = false
133
+ regex = Regexp.new(blacklisted)
134
+
135
+ blacklistedUrl = !!regex.match(request.fullpath)
136
+ blacklistedReferer = !!regex.match(request.referer) if request.referer
137
+
138
+ blacklistedUrl || blacklistedReferer
139
+ }
140
+ return false
141
+ end
142
+
143
+ return is_requesting_prerendered_page
144
+ end
145
+
146
+ def prerenderable_extension(fullpath)
147
+
148
+ path = URI.parse(fullpath).path;
149
+
150
+ # doesn't detect index.whatever.html (multiple dots)
151
+ hasHtmlOrNoExtension = !!path.match(/^(([^.]|\.html?)+)$/);
152
+
153
+ return true if hasHtmlOrNoExtension
154
+
155
+ # hack to handle basenames with multiple dots: index.whatever.html
156
+ endsInHtml = !!path.match(/.html?$/);
157
+
158
+ return true if endsInHtml
159
+
160
+ return false;
161
+
162
+ end
163
+
164
+ def get_prerendered_page_response(env)
165
+ begin
166
+ url = URI.parse(build_api_url(env))
167
+ headers = {
168
+ 'User-Agent' => env['HTTP_USER_AGENT'],
169
+ 'Accept-Encoding' => 'gzip'
170
+ }
171
+ headers['X-Prerender-Token'] = ENV['PRERENDER_TOKEN'] if ENV['PRERENDER_TOKEN']
172
+ headers['X-Prerender-Token'] = @options[:prerender_token] if @options[:prerender_token]
173
+ req = Net::HTTP::Get.new(url.request_uri, headers)
174
+ http = Net::HTTP.new(url.host, url.port)
175
+ http.use_ssl = true if url.scheme == 'https'
176
+ response = http.request(req)
177
+ if response['Content-Encoding'] == 'gzip'
178
+ response.body = ActiveSupport::Gzip.decompress(response.body)
179
+ response['Content-Length'] = response.body.length
180
+ response.delete('Content-Encoding')
181
+ end
182
+ response
183
+ rescue => e
184
+ puts "Error: #{e.class.name}"
185
+ nil
186
+ end
187
+ end
188
+
189
+
190
+ def build_api_url(env)
191
+ new_env = env
192
+ if env["CF-VISITOR"]
193
+ match = /"scheme":"(http|https)"/.match(env['CF-VISITOR'])
194
+ new_env["HTTPS"] = true and new_env["rack.url_scheme"] = "https" and new_env["SERVER_PORT"] = 443 if (match && match[1] == "https")
195
+ new_env["HTTPS"] = false and new_env["rack.url_scheme"] = "http" and new_env["SERVER_PORT"] = 80 if (match && match[1] == "http")
196
+ end
197
+
198
+ if env["X-FORWARDED-PROTO"]
199
+ 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"
200
+ 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"
201
+ end
202
+
203
+ if @options[:protocol]
204
+ new_env["HTTPS"] = true and new_env["rack.url_scheme"] = "https" and new_env["SERVER_PORT"] = 443 if @options[:protocol] == "https"
205
+ new_env["HTTPS"] = false and new_env["rack.url_scheme"] = "http" and new_env["SERVER_PORT"] = 80 if @options[:protocol] == "http"
206
+ end
207
+
208
+ url = Rack::Request.new(new_env).url
209
+ puts "Requesting prerendered page for #{url}"
210
+ prerender_url = get_prerender_service_url()
211
+ forward_slash = prerender_url[-1, 1] == '/' ? '' : '/'
212
+ "#{prerender_url}#{forward_slash}#{url}"
213
+ end
214
+
215
+
216
+ def get_prerender_service_url
217
+ @options[:prerender_service_url] || ENV['PRERENDER_SERVICE_URL'] || 'http://service.prerender.cloud/'
218
+ end
219
+
220
+
221
+ def build_rack_response_from_prerender(prerendered_response)
222
+
223
+ headers = (prerendered_response.respond_to?(:headers) && prerendered_response.headers) || self.class.normalize_headers(prerendered_response.to_hash)
224
+
225
+ response = Rack::Response.new(prerendered_response.body, prerendered_response.code, headers)
226
+
227
+
228
+ @options[:build_rack_response_from_prerender].call(response, prerendered_response) if @options[:build_rack_response_from_prerender]
229
+
230
+ response
231
+ end
232
+
233
+ def before_render(env)
234
+ return nil unless @options[:before_render]
235
+
236
+ cached_render = @options[:before_render].call(env)
237
+
238
+ if cached_render && cached_render.is_a?(String)
239
+ Rack::Response.new(cached_render, 200, { 'Content-Type' => 'text/html; charset=utf-8' })
240
+ elsif cached_render && cached_render.is_a?(Rack::Response)
241
+ cached_render
242
+ else
243
+ nil
244
+ end
245
+ end
246
+
247
+
248
+ def after_render(env, response)
249
+ return true unless @options[:after_render]
250
+ @options[:after_render].call(env, response)
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "prerendercloud"
5
+ spec.version = "0.1.0"
6
+ spec.authors = ["Jonathan Otto"]
7
+ spec.email = ["support@prerender.cloud"]
8
+ spec.description = %q{Rack middleware to server-side render your JavaScript apps by prerender.cloud}
9
+ spec.summary = %q{Rack middleware to server-side render your JavaScript apps by prerender.cloud}
10
+ spec.homepage = "https://github.com/sanfrancesco/prerendercloud-ruby"
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,23 @@
1
+ require 'rack'
2
+
3
+ require_relative './lib/prerendercloud'
4
+
5
+ class RackSampleApp
6
+ def call(env)
7
+ req = Rack::Request.new(env)
8
+ case req.path_info
9
+ when /main.js/
10
+ [200, {"Content-Type" => "application/javascript"}, ["document.getElementById('root').innerHTML = 'js-app';"]]
11
+ else
12
+ [200, {"Content-Type" => "text/html"}, ["<html><body><div id='root'></div><script src='/main.js'></script></body></html>"]]
13
+ end
14
+ end
15
+ end
16
+
17
+
18
+ app = Rack::Builder.new do
19
+ use Rack::Prerendercloud
20
+ run RackSampleApp.new
21
+ end
22
+
23
+ Rack::Handler::WEBrick.run app
@@ -0,0 +1,239 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe Rack::Prerendercloud 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::Prerendercloud.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::Prerendercloud.new(@app).call(request)
21
+
22
+ assert_equal response[2].body, ["<html></html>"]
23
+ assert_equal response[2].status, 301
24
+ assert_equal( { '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::Prerendercloud.new(@app).call(request)
32
+
33
+ assert_equal ["<html></html>"], response[2].body
34
+ end
35
+
36
+ it "should return a prerendered reponse if the url is for .html file" do
37
+ request = Rack::MockRequest.env_for "/path.html", "HTTP_USER_AGENT" => bot
38
+ stub_request(:get, @prerender.build_api_url(request)).with(:headers => { 'User-Agent' => bot }).to_return(:body => "<html></html>")
39
+ response = Rack::Prerendercloud.new(@app).call(request)
40
+
41
+ assert_equal ["<html></html>"], response[2].body
42
+ end
43
+
44
+ it "should continue to app routes if the url is for .js file" do
45
+ request = Rack::MockRequest.env_for "/path.js", "HTTP_USER_AGENT" => bot
46
+ stub_request(:get, @prerender.build_api_url(request)).with(:headers => { 'User-Agent' => bot }).to_return(:body => "<html></html>")
47
+ response = Rack::Prerendercloud.new(@app).call(request)
48
+
49
+ assert_equal "", response[2]
50
+ end
51
+
52
+ it "with bots_only, should continue to app routes if the url is a bad url with _escaped_fragment_" do
53
+ request = Rack::MockRequest.env_for "/path?query=string?_escaped_fragment_=", "HTTP_USER_AGENT" => user
54
+ response = Rack::Prerendercloud.new(@app, bots_only: true).call(request)
55
+
56
+ assert_equal "", response[2]
57
+ end
58
+
59
+
60
+ it "should continue to app routes if the request is not a GET" do
61
+ request = Rack::MockRequest.env_for "/path?_escaped_fragment_=", { "HTTP_USER_AGENT" => user, "REQUEST_METHOD" => "POST" }
62
+ response = Rack::Prerendercloud.new(@app).call(request)
63
+
64
+ assert_equal "", response[2]
65
+ end
66
+
67
+
68
+ it "with bots_only option, should continue to app routes if user is not a bot by checking agent string" do
69
+ request = Rack::MockRequest.env_for "/", "HTTP_USER_AGENT" => user
70
+ response = Rack::Prerendercloud.new(@app, bots_only: true).call(request)
71
+
72
+ assert_equal "", response[2]
73
+ end
74
+
75
+
76
+ it "should continue to app routes if prerendercloud user-agent" do
77
+ request = Rack::MockRequest.env_for "/path?_escaped_fragment_=", "HTTP_USER_AGENT" => 'prerendercloud'
78
+ response = Rack::Prerendercloud.new(@app).call(request)
79
+
80
+ assert_equal "", response[2]
81
+ end
82
+
83
+
84
+ it "should continue to app routes if user is a bot, but the bot is requesting a resource file" do
85
+ request = Rack::MockRequest.env_for "/main.js?anyQueryParam=true", "HTTP_USER_AGENT" => bot
86
+ response = Rack::Prerendercloud.new(@app).call(request)
87
+
88
+ assert_equal "", response[2]
89
+ end
90
+
91
+
92
+ it "should continue to app routes if the url is not part of the regex specific whitelist" do
93
+ request = Rack::MockRequest.env_for "/saved/search/blah?_escaped_fragment_=", "HTTP_USER_AGENT" => bot
94
+ response = Rack::Prerendercloud.new(@app, whitelist: ['^/search', '/help']).call(request)
95
+
96
+ assert_equal "", response[2]
97
+ end
98
+
99
+
100
+ it "should set use_ssl to true for https prerender_service_url" do
101
+ @prerender = Rack::Prerendercloud.new(@app, prerender_service_url: 'https://service.prerender.cloud/')
102
+
103
+ request = Rack::MockRequest.env_for "/search/things/123/page?_escaped_fragment_=", "HTTP_USER_AGENT" => bot
104
+ stub_request(:get, @prerender.build_api_url(request)).to_return(:body => "<html></html>")
105
+ response = @prerender.call(request)
106
+
107
+ assert_equal ["<html></html>"], response[2].body
108
+ end
109
+
110
+
111
+ it "should return a prerendered response if the url is part of the regex specific whitelist" do
112
+ request = Rack::MockRequest.env_for "/search/things/123/page?_escaped_fragment_=", "HTTP_USER_AGENT" => bot
113
+ stub_request(:get, @prerender.build_api_url(request)).to_return(:body => "<html></html>")
114
+ response = Rack::Prerendercloud.new(@app, whitelist: ['^/search.*page', '/help']).call(request)
115
+
116
+ assert_equal ["<html></html>"], response[2].body
117
+ end
118
+
119
+
120
+ it "should continue to app routes if the url is part of the regex specific blacklist" do
121
+ request = Rack::MockRequest.env_for "/search/things/123/page", "HTTP_USER_AGENT" => bot
122
+ response = Rack::Prerendercloud.new(@app, blacklist: ['^/search', '/help']).call(request)
123
+
124
+ assert_equal "", response[2]
125
+ end
126
+
127
+ it "should continue to app routes if the hashbang url is part of the regex specific blacklist" do
128
+ request = Rack::MockRequest.env_for "?_escaped_fragment_=/search/things/123/page", "HTTP_USER_AGENT" => bot
129
+ response = Rack::Prerendercloud.new(@app, blacklist: ['/search', '/help']).call(request)
130
+
131
+ assert_equal "", response[2]
132
+ end
133
+
134
+ it "should return a prerendered response if the url is not part of the regex specific blacklist" do
135
+ request = Rack::MockRequest.env_for "/profile/search/blah", "HTTP_USER_AGENT" => bot
136
+ stub_request(:get, @prerender.build_api_url(request)).to_return(:body => "<html></html>")
137
+ response = Rack::Prerendercloud.new(@app, blacklist: ['^/search', '/help']).call(request)
138
+
139
+ assert_equal ["<html></html>"], response[2].body
140
+ end
141
+
142
+
143
+ it "should continue to app routes if the referer is part of the regex specific blacklist" do
144
+ request = Rack::MockRequest.env_for "/api/results", "HTTP_USER_AGENT" => bot, "HTTP_REFERER" => '/search'
145
+ response = Rack::Prerendercloud.new(@app, blacklist: ['^/search', '/help']).call(request)
146
+
147
+ assert_equal "", response[2]
148
+ end
149
+
150
+
151
+ it "should return a prerendered response if the referer is not part of the regex specific blacklist" do
152
+ request = Rack::MockRequest.env_for "/api/results", "HTTP_USER_AGENT" => bot, "HTTP_REFERER" => '/profile/search'
153
+ stub_request(:get, @prerender.build_api_url(request)).to_return(:body => "<html></html>")
154
+ response = Rack::Prerendercloud.new(@app, blacklist: ['^/search', '/help']).call(request)
155
+
156
+ assert_equal ["<html></html>"], response[2].body
157
+ end
158
+
159
+
160
+ it "should return a prerendered response if a string is returned from before_render" do
161
+ request = Rack::MockRequest.env_for "/", "HTTP_USER_AGENT" => bot
162
+ response = Rack::Prerendercloud.new(@app, before_render: Proc.new do |env| '<html>cached</html>' end).call(request)
163
+
164
+ assert_equal ["<html>cached</html>"], response[2].body
165
+ end
166
+
167
+
168
+ it "should return a prerendered response if a response is returned from before_render" do
169
+ request = Rack::MockRequest.env_for "/", "HTTP_USER_AGENT" => bot
170
+ response = Rack::Prerendercloud.new(@app, before_render: Proc.new do |env| Rack::Response.new('<html>cached2</html>', 200, { 'test' => 'test2Header'}) end).call(request)
171
+
172
+ assert_equal ["<html>cached2</html>"], response[2].body
173
+ assert_equal response[2].status, 200
174
+ assert_equal( { 'test' => 'test2Header', "Content-Length"=>"20"}, response[2].headers )
175
+ end
176
+
177
+
178
+ describe '#buildApiUrl' do
179
+ it "should build the correct api url with the default url" do
180
+ request = Rack::MockRequest.env_for "https://google.com/search?q=javascript"
181
+ ENV['PRERENDER_SERVICE_URL'] = nil
182
+ assert_equal 'http://service.prerender.cloud/https://google.com/search?q=javascript', @prerender.build_api_url(request)
183
+ end
184
+
185
+
186
+ it "should build the correct api url with an environment variable url" do
187
+ ENV['PRERENDER_SERVICE_URL'] = 'http://prerenderurl.com'
188
+ request = Rack::MockRequest.env_for "https://google.com/search?q=javascript"
189
+ assert_equal 'http://prerenderurl.com/https://google.com/search?q=javascript', @prerender.build_api_url(request)
190
+ ENV['PRERENDER_SERVICE_URL'] = nil
191
+ end
192
+
193
+
194
+ it "should build the correct api url with an initialization variable url" do
195
+ @prerender = Rack::Prerendercloud.new(@app, prerender_service_url: 'http://prerenderurl.com')
196
+ request = Rack::MockRequest.env_for "https://google.com/search?q=javascript"
197
+ assert_equal 'http://prerenderurl.com/https://google.com/search?q=javascript', @prerender.build_api_url(request)
198
+ end
199
+
200
+
201
+ it "should build the correct https api url with an initialization variable url" do
202
+ @prerender = Rack::Prerendercloud.new(@app, prerender_service_url: 'https://prerenderurl.com')
203
+ request = Rack::MockRequest.env_for "https://google.com/search?q=javascript"
204
+ assert_equal 'https://prerenderurl.com/https://google.com/search?q=javascript', @prerender.build_api_url(request)
205
+ end
206
+
207
+
208
+ # Check CF-Visitor header in order to Work behind CloudFlare with Flexible SSL (https://support.cloudflare.com/hc/en-us/articles/200170536)
209
+ it "should build the correct api url for the Cloudflare Flexible SSL support" do
210
+ request = Rack::MockRequest.env_for "http://google.com/search?q=javascript", { 'CF-VISITOR' => '"scheme":"https"'}
211
+ ENV['PRERENDER_SERVICE_URL'] = nil
212
+ assert_equal 'http://service.prerender.cloud/https://google.com/search?q=javascript', @prerender.build_api_url(request)
213
+ end
214
+
215
+
216
+ # Check X-Forwarded-Proto because Heroku SSL Support terminates at the load balancer
217
+ it "should build the correct api url for the Heroku SSL Addon support with single value" do
218
+ request = Rack::MockRequest.env_for "http://google.com/search?q=javascript", { 'X-FORWARDED-PROTO' => 'https'}
219
+ ENV['PRERENDER_SERVICE_URL'] = nil
220
+ assert_equal 'http://service.prerender.cloud/https://google.com/search?q=javascript', @prerender.build_api_url(request)
221
+ end
222
+
223
+
224
+ # Check X-Forwarded-Proto because Heroku SSL Support terminates at the load balancer
225
+ it "should build the correct api url for the Heroku SSL Addon support with double value" do
226
+ request = Rack::MockRequest.env_for "http://google.com/search?q=javascript", { 'X-FORWARDED-PROTO' => 'https,http'}
227
+ ENV['PRERENDER_SERVICE_URL'] = nil
228
+ assert_equal 'http://service.prerender.cloud/https://google.com/search?q=javascript', @prerender.build_api_url(request)
229
+ end
230
+
231
+ it "should build the correct api url for the protocol option" do
232
+ @prerender = Rack::Prerendercloud.new(@app, protocol: 'https')
233
+ request = Rack::MockRequest.env_for "http://google.com/search?q=javascript"
234
+ ENV['PRERENDER_SERVICE_URL'] = nil
235
+ assert_equal 'http://service.prerender.cloud/https://google.com/search?q=javascript', @prerender.build_api_url(request)
236
+ end
237
+ end
238
+
239
+ end
@@ -0,0 +1,4 @@
1
+ require 'minitest/autorun'
2
+ require 'webmock/minitest'
3
+ require 'rack'
4
+ require File.expand_path('../../lib/prerendercloud.rb', __FILE__)
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prerendercloud
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Otto
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Rack middleware to server-side render your JavaScript apps by prerender.cloud
84
+ email:
85
+ - support@prerender.cloud
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE
94
+ - README.md
95
+ - Rakefile
96
+ - lib/prerendercloud.rb
97
+ - prerendercloud.gemspec
98
+ - rack_sample_app.rb
99
+ - test/lib/prerendercloud.rb
100
+ - test/test_helper.rb
101
+ homepage: https://github.com/sanfrancesco/prerendercloud-ruby
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.6.10
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Rack middleware to server-side render your JavaScript apps by prerender.cloud
125
+ test_files:
126
+ - test/lib/prerendercloud.rb
127
+ - test/test_helper.rb