rack-prerender 1.6.2.1 → 1.6.2.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.
- checksums.yaml +4 -4
- data/lib/rack/prerender/constraint.rb +13 -10
- data/lib/rack/prerender/fetcher.rb +92 -39
- data/lib/rack/prerender/recache_job.rb +19 -0
- data/lib/rack/prerender/recacher.rb +29 -0
- data/lib/rack/prerender/version.rb +1 -1
- data/lib/rack/prerender.rb +19 -1
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e647c21bd7b1376d4fe43b9ca37c8f59b03c46e6d4e8552300666415781cd758
|
4
|
+
data.tar.gz: e4e9bb35c6cd6770438df47cbc8d4b34f28c42c613216b8b4767e26e746d157e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5fbcd1f83fb88c38bbd8a402c056dee3493785d192f83e493820f0626cc58ee95fe15b889893901929f024f5675b58ea547175a5c1aa543d20cc3ed25dbe235c
|
7
|
+
data.tar.gz: 452d3a01ecd28f604a5a11765ceb054cf2256b48c31d2c888fcf272e84e3db9c6ad7b527012147d4fd8b0bb4f264c1938fe3eee1b9e9b8dac1a97eceac8c06db
|
@@ -10,13 +10,14 @@ module Rack
|
|
10
10
|
@extensions_to_ignore = cast_to_regexp(opts[:extensions_to_ignore] || EXTENSIONS_TO_IGNORE)
|
11
11
|
end
|
12
12
|
|
13
|
+
# This is run on every request, so performance matters here.
|
13
14
|
def matches?(env)
|
14
15
|
return false if env['REQUEST_METHOD'] != 'GET' ||
|
15
16
|
env['HTTP_X_PRERENDER'] ||
|
16
17
|
(user_agent = env['HTTP_USER_AGENT']).nil?
|
17
18
|
|
18
19
|
query = env['QUERY_STRING'].to_s
|
19
|
-
return false unless crawler_user_agents.match?(user_agent
|
20
|
+
return false unless crawler_user_agents.match?(user_agent) ||
|
20
21
|
env['HTTP_X_BUFFERBOT'] ||
|
21
22
|
query.include?('_escaped_fragment_')
|
22
23
|
|
@@ -25,7 +26,7 @@ module Rack
|
|
25
26
|
return false if extensions_to_ignore.match?(fullpath)
|
26
27
|
return false if whitelist && !whitelist.match?(fullpath)
|
27
28
|
return false if blacklist && (blacklist.match?(fullpath) ||
|
28
|
-
blacklist.match?(env['HTTP_REFERER'].to_s
|
29
|
+
blacklist.match?(env['HTTP_REFERER'].to_s))
|
29
30
|
|
30
31
|
true
|
31
32
|
end
|
@@ -33,14 +34,16 @@ module Rack
|
|
33
34
|
private
|
34
35
|
|
35
36
|
def cast_to_regexp(list_arg, escape: true)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
37
|
+
regexp =
|
38
|
+
case list_arg
|
39
|
+
when Regexp, nil
|
40
|
+
list_arg
|
41
|
+
when Array
|
42
|
+
escape ? Regexp.union(list_arg) : Regexp.new(list_arg.join('|'))
|
43
|
+
else
|
44
|
+
Regexp.new(escape ? Regexp.escape(list_arg) : list_arg)
|
45
|
+
end
|
46
|
+
regexp && Regexp.new(regexp.source, Regexp::IGNORECASE)
|
44
47
|
end
|
45
48
|
|
46
49
|
CRAWLER_USER_AGENTS = [
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class Prerender
|
3
5
|
class Fetcher
|
@@ -7,18 +9,31 @@ module Rack
|
|
7
9
|
@options = options
|
8
10
|
end
|
9
11
|
|
12
|
+
# Entry point for automatic fetching via the middleware.
|
10
13
|
def call(env)
|
11
14
|
cached_response = before_render(env)
|
12
15
|
return cached_response.finish if cached_response
|
13
16
|
|
14
|
-
if prerendered_response =
|
17
|
+
if prerendered_response = fetch_env(env)
|
15
18
|
response = build_rack_response_from_prerender(prerendered_response)
|
16
19
|
after_render(env, prerendered_response)
|
17
20
|
return response.finish
|
18
21
|
end
|
22
|
+
|
19
23
|
nil
|
20
24
|
end
|
21
25
|
|
26
|
+
# Entry point for manual fetching. Does not run callbacks.
|
27
|
+
def fetch(arg)
|
28
|
+
case arg
|
29
|
+
when String, Symbol, URI then fetch_url(arg.to_s)
|
30
|
+
when Hash then fetch_env(arg)
|
31
|
+
when Rack::Request then fetch_env(arg.env)
|
32
|
+
else
|
33
|
+
raise ArgumentError, "expected a URL, Request or env, got #{arg.class}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
22
37
|
def before_render(env)
|
23
38
|
return unless callback = options[:before_render]
|
24
39
|
|
@@ -33,60 +48,98 @@ module Rack
|
|
33
48
|
end
|
34
49
|
end
|
35
50
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
if
|
50
|
-
|
51
|
-
response['Content-Length'] = response.body.bytesize
|
52
|
-
response.delete('Content-Encoding')
|
51
|
+
def fetch_env(env)
|
52
|
+
fetch_url(request_url(env), as: env['HTTP_USER_AGENT'])
|
53
|
+
end
|
54
|
+
|
55
|
+
def fetch_url(url, as: nil)
|
56
|
+
uri = URI.parse(api_url(url))
|
57
|
+
fetch_api_uri(uri, as: as)
|
58
|
+
end
|
59
|
+
|
60
|
+
# This is just horrible, but replacing net/http would break compatibility
|
61
|
+
# because the response object is leaked to several callbacks :(
|
62
|
+
def fetch_api_uri(uri, as: nil)
|
63
|
+
req = Net::HTTP::Get.new(uri.request_uri, headers(user_agent: as))
|
64
|
+
if options[:basic_auth]
|
65
|
+
req.basic_auth(ENV['PRERENDER_USERNAME'], ENV['PRERENDER_PASSWORD'])
|
53
66
|
end
|
54
|
-
|
67
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
68
|
+
http.use_ssl = true if uri.scheme == 'https'
|
69
|
+
response = http.request(req)
|
70
|
+
decompress(response)
|
55
71
|
rescue
|
56
72
|
nil
|
57
73
|
end
|
58
74
|
|
59
|
-
def api_url(
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
new_env['HTTPS'] = false and new_env['rack.url_scheme'] = "http" and new_env['SERVER_PORT'] = 80 if (match && match[1] == 'http')
|
75
|
+
def api_url(url)
|
76
|
+
if service_url.match?(/[=\/]$/)
|
77
|
+
"#{service_url}#{url}"
|
78
|
+
else
|
79
|
+
"#{service_url}/#{url}"
|
65
80
|
end
|
81
|
+
end
|
66
82
|
|
67
|
-
|
68
|
-
|
69
|
-
|
83
|
+
def service_url
|
84
|
+
options[:prerender_service_url] || ENV['PRERENDER_SERVICE_URL'] ||
|
85
|
+
'http://service.prerender.io'
|
86
|
+
end
|
87
|
+
|
88
|
+
def headers(user_agent: nil)
|
89
|
+
{
|
90
|
+
'Accept-Encoding' => 'gzip',
|
91
|
+
'User-Agent' => user_agent,
|
92
|
+
'X-Prerender-Token' => token,
|
93
|
+
}.compact
|
94
|
+
end
|
95
|
+
|
96
|
+
def token
|
97
|
+
options[:prerender_token] || ENV['PRERENDER_TOKEN']
|
98
|
+
end
|
99
|
+
|
100
|
+
def request_url(env)
|
101
|
+
if env['CF-VISITOR'] && protocol = env['CF-VISITOR'][/"scheme":"(http|https)"/, 1]
|
102
|
+
configure_protocol(env, protocol)
|
103
|
+
end
|
104
|
+
|
105
|
+
if env['X-FORWARDED-PROTO'] && protocol = env["X-FORWARDED-PROTO"].split(',')[0]
|
106
|
+
configure_protocol(env, protocol)
|
70
107
|
end
|
71
108
|
|
72
|
-
if options[:protocol]
|
73
|
-
|
74
|
-
new_env['HTTPS'] = false and new_env['rack.url_scheme'] = "http" and new_env['SERVER_PORT'] = 80 if options[:protocol] == 'http'
|
109
|
+
if protocol = options[:protocol]
|
110
|
+
configure_protocol(env, protocol)
|
75
111
|
end
|
76
112
|
|
77
|
-
|
78
|
-
prerender_url = prerender_service_url()
|
79
|
-
forward_slash = prerender_url[-1, 1] == '/' ? '' : '/'
|
80
|
-
"#{prerender_url}#{forward_slash}#{url}"
|
113
|
+
Rack::Request.new(env).url
|
81
114
|
end
|
82
115
|
|
83
|
-
def
|
84
|
-
|
116
|
+
def configure_protocol(env, protocol)
|
117
|
+
return unless protocol == 'http' || protocol == 'https'
|
118
|
+
|
119
|
+
env['rack.url_scheme'] = protocol
|
120
|
+
env['HTTPS'] = protocol == 'https'
|
121
|
+
env['SERVER_PORT'] = protocol == 'https' ? 443 : 80
|
122
|
+
end
|
123
|
+
|
124
|
+
def decompress(response)
|
125
|
+
if response['Content-Encoding'] == 'gzip'
|
126
|
+
response.body =
|
127
|
+
Zlib::GzipReader.wrap(StringIO.new(response.body), &:read)
|
128
|
+
response['Content-Length'] = response.body.bytesize
|
129
|
+
response.delete('Content-Encoding')
|
130
|
+
end
|
131
|
+
response
|
85
132
|
end
|
86
133
|
|
87
134
|
def build_rack_response_from_prerender(prerendered_response)
|
88
|
-
response = Rack::Response.new(
|
89
|
-
|
135
|
+
response = Rack::Response.new(
|
136
|
+
prerendered_response.body,
|
137
|
+
prerendered_response.code,
|
138
|
+
prerendered_response,
|
139
|
+
)
|
140
|
+
if callback = options[:build_rack_response_from_prerender]
|
141
|
+
callback.call(response, prerendered_response)
|
142
|
+
end
|
90
143
|
response
|
91
144
|
end
|
92
145
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rack
|
2
|
+
class Prerender
|
3
|
+
RecacheJob =
|
4
|
+
if defined?(::ActiveJob::Base)
|
5
|
+
Class.new(::ActiveJob::Base)
|
6
|
+
elsif defined?(::Sidekiq::Worker)
|
7
|
+
Class.new do
|
8
|
+
include ::Sidekiq::Worker
|
9
|
+
instance_eval { alias perform_later perform_async }
|
10
|
+
end
|
11
|
+
else
|
12
|
+
raise NameError, 'requires ActiveJob or Sidekiq'
|
13
|
+
end.class_eval do
|
14
|
+
def perform(url, options)
|
15
|
+
::Rack::Prerender::Recacher.new(options).call(url)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rack
|
2
|
+
class Prerender
|
3
|
+
class Recacher
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(cached_url)
|
11
|
+
uri = URI(api_url)
|
12
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
13
|
+
request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
14
|
+
request.body = %({"prerenderToken":"#{token}","url":"#{cached_url}"})
|
15
|
+
http.request(request) # => Net::HTTPResponse object
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def api_url
|
20
|
+
options[:prerender_recache_url] || ENV['PRERENDER_RECACHE_URL'] ||
|
21
|
+
'http://api.prerender.io/recache'
|
22
|
+
end
|
23
|
+
|
24
|
+
def token
|
25
|
+
options[:prerender_token] || ENV['PRERENDER_TOKEN']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rack/prerender.rb
CHANGED
@@ -3,18 +3,36 @@ module Rack
|
|
3
3
|
require 'net/http'
|
4
4
|
require_relative 'prerender/constraint'
|
5
5
|
require_relative 'prerender/fetcher'
|
6
|
+
require_relative 'prerender/recacher'
|
6
7
|
require_relative 'prerender/version'
|
7
8
|
|
8
|
-
|
9
|
+
attr_accessor :app, :constraint, :fetcher
|
9
10
|
|
10
11
|
def initialize(app, options = {})
|
11
12
|
@app = app
|
12
13
|
@constraint = Constraint.new(options)
|
13
14
|
@fetcher = Fetcher.new(options)
|
15
|
+
@@options = options
|
14
16
|
end
|
15
17
|
|
16
18
|
def call(env)
|
17
19
|
constraint.matches?(env) && fetcher.call(env) || app.call(env)
|
18
20
|
end
|
21
|
+
|
22
|
+
# utility methods
|
23
|
+
|
24
|
+
def self.fetch(arg, **options)
|
25
|
+
Fetcher.new(@@options.to_h.merge(options)).fetch(arg)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.recache_now(url, **options)
|
29
|
+
Recacher.new(@@options.to_h.merge(options)).call(url)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.recache_later(url, **options)
|
33
|
+
# require on demand, so ActiveJob/Sidekiq can come later in load order
|
34
|
+
require_relative 'prerender/recache_job'
|
35
|
+
RecacheJob.perform_later(url, @@options.to_h.merge(options))
|
36
|
+
end
|
19
37
|
end
|
20
38
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-prerender
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.2.
|
4
|
+
version: 1.6.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Todd Hooper
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-
|
12
|
+
date: 2020-04-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -25,6 +25,20 @@ dependencies:
|
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: activejob
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: bundler
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,6 +53,20 @@ dependencies:
|
|
39
53
|
- - ">="
|
40
54
|
- !ruby/object:Gem::Version
|
41
55
|
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: byebug
|
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'
|
42
70
|
- !ruby/object:Gem::Dependency
|
43
71
|
name: rake
|
44
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -93,6 +121,8 @@ files:
|
|
93
121
|
- lib/rack/prerender.rb
|
94
122
|
- lib/rack/prerender/constraint.rb
|
95
123
|
- lib/rack/prerender/fetcher.rb
|
124
|
+
- lib/rack/prerender/recache_job.rb
|
125
|
+
- lib/rack/prerender/recacher.rb
|
96
126
|
- lib/rack/prerender/version.rb
|
97
127
|
homepage: https://github.com/jaynetics/rack-prerender
|
98
128
|
licenses:
|