rewritten 0.15.2 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +12 -0
- data/.rubocop_todo.yml +116 -0
- data/Gemfile +1 -1
- data/HISTORY.rdoc +4 -0
- data/Rakefile +1 -5
- data/bin/rewritten-dump.rb +11 -14
- data/bin/rewritten-import.rb +9 -12
- data/bin/rewritten-web.rb +7 -9
- data/config.ru +0 -1
- data/lib/rack/canonical.rb +6 -14
- data/lib/rack/dummy.rb +7 -14
- data/lib/rack/html.rb +4 -13
- data/lib/rack/record.rb +3 -9
- data/lib/rack/subdomain.rb +5 -13
- data/lib/rack/url.rb +19 -24
- data/lib/rewritten.rb +45 -69
- data/lib/rewritten/config.ru +0 -1
- data/lib/rewritten/document.rb +6 -7
- data/lib/rewritten/helpers.rb +0 -1
- data/lib/rewritten/rails/url_helpers.rb +0 -3
- data/lib/rewritten/server.rb +58 -73
- data/lib/rewritten/server/test_helper.rb +2 -2
- data/lib/rewritten/version.rb +1 -2
- data/rewritten.gemspec +19 -22
- data/test/rack/rewritten_canonical_test.rb +23 -31
- data/test/rack/rewritten_html_test.rb +16 -21
- data/test/rack/rewritten_url_test.rb +77 -95
- data/test/rewritten/document_test.rb +28 -28
- data/test/rewritten_test.rb +25 -36
- data/test/test_helper.rb +2 -4
- metadata +4 -2
data/lib/rack/subdomain.rb
CHANGED
@@ -1,39 +1,31 @@
|
|
1
1
|
require 'rack'
|
2
2
|
|
3
3
|
module Rack
|
4
|
-
|
5
4
|
module Rewritten
|
6
|
-
|
7
5
|
class Subdomain
|
8
|
-
|
9
6
|
def initialize(app, *fqdns)
|
10
7
|
@app = app
|
11
8
|
@fqdns = fqdns
|
12
9
|
end
|
13
10
|
|
14
11
|
def call(env)
|
15
|
-
puts
|
12
|
+
puts '-> Rack::Rewritten::Subdomain'
|
16
13
|
req = Rack::Request.new(env)
|
17
14
|
|
18
15
|
@fqdns.each do |n|
|
19
16
|
if req.host =~ /(.+)\.#{n}$/
|
20
|
-
if
|
17
|
+
if Regexp.last_match(1) == 'www'
|
21
18
|
break
|
22
19
|
else
|
23
|
-
env[
|
24
|
-
env[
|
20
|
+
env['SUBDOMAIN'] = Regexp.last_match(1)
|
21
|
+
env['FQDN'] = n
|
25
22
|
break
|
26
23
|
end
|
27
24
|
end
|
28
25
|
end
|
29
26
|
|
30
|
-
@app.call(env)
|
27
|
+
@app.call(env)
|
31
28
|
end
|
32
|
-
|
33
29
|
end
|
34
|
-
|
35
30
|
end
|
36
|
-
|
37
31
|
end
|
38
|
-
|
39
|
-
|
data/lib/rack/url.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
require 'rack'
|
2
2
|
|
3
3
|
module Rack
|
4
|
-
|
5
4
|
module Rewritten
|
6
|
-
|
7
5
|
class Url
|
8
|
-
|
9
6
|
attr_accessor :base_url
|
10
7
|
|
11
8
|
def initialize(app, &block)
|
@@ -17,37 +14,39 @@ module Rack
|
|
17
14
|
instance_eval(&block) if block_given?
|
18
15
|
end
|
19
16
|
|
20
|
-
def
|
21
|
-
url.nil?
|
17
|
+
def internal_target?(url)
|
18
|
+
url.nil? || url.start_with?('/') || url.start_with?(@base_url)
|
22
19
|
end
|
23
20
|
|
24
|
-
def
|
25
|
-
!
|
21
|
+
def external_target?(url)
|
22
|
+
!internal_target?(url)
|
26
23
|
end
|
27
24
|
|
28
25
|
def call(env)
|
29
26
|
req = Rack::Request.new(env)
|
30
27
|
|
31
|
-
subdomain = env[
|
28
|
+
subdomain = env['SUBDOMAIN'] ? "#{env['SUBDOMAIN']}:" : ''
|
32
29
|
|
33
30
|
path = "#{subdomain}#{req.path_info}"
|
34
31
|
path.downcase! if downcase_before_lookup?
|
35
32
|
|
36
33
|
target_url = ::Rewritten.translate(path)
|
37
34
|
|
38
|
-
if
|
35
|
+
if external_target?(target_url)
|
39
36
|
r = Rack::Response.new
|
40
37
|
r.redirect(target_url, 301)
|
41
38
|
r.finish
|
42
|
-
elsif ::Rewritten.includes?(path.chomp(
|
39
|
+
elsif ::Rewritten.includes?(req.fullpath) || ::Rewritten.includes?(path.chomp('/')) || backwards = (translate_backwards? && ::Rewritten.exist_translation_for?(path))
|
43
40
|
|
44
|
-
to = ::Rewritten.includes?(
|
41
|
+
to = ::Rewritten.includes?(req.fullpath)
|
42
|
+
to ||= ::Rewritten.includes?(path.chomp('/')) || path
|
45
43
|
|
46
44
|
current_path = ::Rewritten.get_current_translation(to)
|
47
|
-
current_path = current_path.split(
|
45
|
+
current_path = current_path.split(':').last
|
46
|
+
current_path_with_query = current_path
|
48
47
|
current_path = current_path.split('?')[0]
|
49
48
|
|
50
|
-
if (current_path == req.path_info)
|
49
|
+
if (req.fullpath == current_path_with_query || current_path == req.path_info) || (::Rewritten.base_from(req.path_info) == current_path) || ::Rewritten.flag?(path, 'L')
|
51
50
|
# if this is the current path, rewrite path and parameters
|
52
51
|
tpath, tparams = split_to_path_params(to)
|
53
52
|
env['QUERY_STRING'] = Rack::Utils.build_nested_query(tparams.merge(req.params))
|
@@ -59,12 +58,12 @@ module Rack
|
|
59
58
|
|
60
59
|
r = Rack::Response.new
|
61
60
|
|
62
|
-
new_path = env[
|
63
|
-
new_path <<
|
64
|
-
new_path << env[
|
61
|
+
new_path = env['rack.url_scheme'].dup
|
62
|
+
new_path << '://'
|
63
|
+
new_path << env['HTTP_HOST'].dup.sub(/^#{subdomain.chomp(':')}\./, '')
|
65
64
|
new_path << current_path
|
66
65
|
new_path << ::Rewritten.appendix(path) unless backwards
|
67
|
-
new_path << '?' << env[
|
66
|
+
new_path << '?' << env['QUERY_STRING'] unless (env['QUERY_STRING'] || '').empty?
|
68
67
|
|
69
68
|
r.redirect(new_path, 301)
|
70
69
|
a = r.finish
|
@@ -85,24 +84,20 @@ module Rack
|
|
85
84
|
@translate_backwards
|
86
85
|
end
|
87
86
|
|
88
|
-
|
89
|
-
@translate_backwards = yes_or_no
|
90
|
-
end
|
87
|
+
attr_writer :translate_backwards
|
91
88
|
|
92
89
|
def downcase_before_lookup?
|
93
90
|
@downcase_before_lookup
|
94
91
|
end
|
95
92
|
|
96
|
-
|
97
|
-
@downcase_before_lookup = yes_or_no
|
98
|
-
end
|
93
|
+
attr_writer :downcase_before_lookup
|
99
94
|
|
100
95
|
def translate_partial?
|
101
96
|
@translate_partial
|
102
97
|
end
|
103
98
|
|
104
99
|
def translate_partial=(yes_or_no)
|
105
|
-
$stderr.puts
|
100
|
+
$stderr.puts 'DEPRECATED. Please use Rewritten.translate_partial'
|
106
101
|
::Rewritten.translate_partial = yes_or_no
|
107
102
|
end
|
108
103
|
end
|
data/lib/rewritten.rb
CHANGED
@@ -24,26 +24,24 @@ module Rewritten
|
|
24
24
|
case server
|
25
25
|
when String
|
26
26
|
if server =~ /redis\:\/\//
|
27
|
-
redis = Redis.connect(:
|
27
|
+
redis = Redis.connect(url: server, thread_safe: true)
|
28
28
|
else
|
29
29
|
server, namespace = server.split('/', 2)
|
30
30
|
host, port, db = server.split(':')
|
31
|
-
redis = Redis.new(:
|
32
|
-
|
31
|
+
redis = Redis.new(host: host, port: port,
|
32
|
+
thread_safe: true, db: db)
|
33
33
|
end
|
34
34
|
namespace ||= :rewritten
|
35
35
|
|
36
|
-
@redis = Redis::Namespace.new(namespace, :
|
36
|
+
@redis = Redis::Namespace.new(namespace, redis: redis)
|
37
37
|
when Redis::Namespace
|
38
38
|
@redis = server
|
39
39
|
else
|
40
|
-
@redis = Redis::Namespace.new(:rewritten, :
|
40
|
+
@redis = Redis::Namespace.new(:rewritten, redis: server)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
|
-
@translate_partial = yes_or_no
|
46
|
-
end
|
44
|
+
attr_writer :translate_partial
|
47
45
|
|
48
46
|
def translate_partial?
|
49
47
|
@translate_partial
|
@@ -53,8 +51,8 @@ module Rewritten
|
|
53
51
|
# create a new one.
|
54
52
|
def redis
|
55
53
|
return @redis if @redis
|
56
|
-
self.redis = Redis.respond_to?(:connect) ? Redis.connect :
|
57
|
-
|
54
|
+
self.redis = Redis.respond_to?(:connect) ? Redis.connect : 'localhost:6379'
|
55
|
+
redis
|
58
56
|
end
|
59
57
|
|
60
58
|
def redis_id
|
@@ -62,7 +60,7 @@ module Rewritten
|
|
62
60
|
if redis.respond_to?(:server)
|
63
61
|
redis.server
|
64
62
|
elsif redis.respond_to?(:nodes) # distributed
|
65
|
-
redis.nodes.map
|
63
|
+
redis.nodes.map(&:id).join(', ')
|
66
64
|
else
|
67
65
|
redis.client.id
|
68
66
|
end
|
@@ -81,9 +79,7 @@ module Rewritten
|
|
81
79
|
|
82
80
|
# Set a proc that will be called in the parent process before the
|
83
81
|
# worker forks for the first time.
|
84
|
-
|
85
|
-
@before_first_fork = before_first_fork
|
86
|
-
end
|
82
|
+
attr_writer :before_first_fork
|
87
83
|
|
88
84
|
# The `before_fork` hook will be run in the **parent** process
|
89
85
|
# before every job, so be careful- any changes you make will be
|
@@ -96,9 +92,7 @@ module Rewritten
|
|
96
92
|
end
|
97
93
|
|
98
94
|
# Set the before_fork proc.
|
99
|
-
|
100
|
-
@before_fork = before_fork
|
101
|
-
end
|
95
|
+
attr_writer :before_fork
|
102
96
|
|
103
97
|
# The `after_fork` hook will be run in the child process and is passed
|
104
98
|
# the current job. Any changes you make, therefore, will only live as
|
@@ -111,9 +105,7 @@ module Rewritten
|
|
111
105
|
end
|
112
106
|
|
113
107
|
# Set the after_fork proc.
|
114
|
-
|
115
|
-
@after_fork = after_fork
|
116
|
-
end
|
108
|
+
attr_writer :after_fork
|
117
109
|
|
118
110
|
def to_s
|
119
111
|
"Rewritten Client connected to #{redis_id}"
|
@@ -127,12 +119,10 @@ module Rewritten
|
|
127
119
|
end
|
128
120
|
alias_method :inline, :inline?
|
129
121
|
|
130
|
-
|
131
|
-
@inline = inline
|
132
|
-
end
|
122
|
+
attr_writer :inline
|
133
123
|
|
134
124
|
#
|
135
|
-
# translations
|
125
|
+
# translations
|
136
126
|
#
|
137
127
|
|
138
128
|
def add_translation(line, to)
|
@@ -143,14 +133,14 @@ module Rewritten
|
|
143
133
|
redis.hset("from:#{from}", :to, to)
|
144
134
|
redis.hset("from:#{from}", :flags, flags) if flags
|
145
135
|
|
146
|
-
redis.sadd(:froms, from)
|
147
|
-
redis.sadd(:tos, to)
|
136
|
+
redis.sadd(:froms, from)
|
137
|
+
redis.sadd(:tos, to)
|
148
138
|
score = redis.zcard("to:#{to}") || 0
|
149
|
-
redis.zadd("to:#{to}", score, from)
|
139
|
+
redis.zadd("to:#{to}", score, from)
|
150
140
|
end
|
151
141
|
|
152
142
|
def add_translations(to, froms)
|
153
|
-
froms.each {|from| add_translation(from, to)}
|
143
|
+
froms.each { |from| add_translation(from, to) }
|
154
144
|
end
|
155
145
|
|
156
146
|
def num_translations(to)
|
@@ -195,29 +185,26 @@ module Rewritten
|
|
195
185
|
Rewritten.redis.zrange("to:#{to}", 0, -1)
|
196
186
|
end
|
197
187
|
|
198
|
-
def get_current_translation(path, tail=nil)
|
199
|
-
|
188
|
+
def get_current_translation(path, tail = nil)
|
200
189
|
uri = URI.parse(path)
|
201
190
|
|
202
191
|
# find directly
|
203
192
|
translation = Rewritten.z_range("to:#{path}", -1)
|
204
|
-
|
205
|
-
unless translation
|
206
|
-
translation = Rewritten.z_range("to:#{uri.path}", -1)
|
207
|
-
end
|
193
|
+
|
194
|
+
translation = Rewritten.z_range("to:#{uri.path}", -1) unless translation
|
208
195
|
|
209
196
|
if translation.nil?
|
210
197
|
if translate_partial? && path.count('/') > 1
|
211
198
|
parts = path.split('/')
|
212
|
-
shorter_path = parts.slice(0, parts.size-1).join('/')
|
213
|
-
appendix = parts.last + (tail ?
|
214
|
-
return get_current_translation(shorter_path, appendix)
|
199
|
+
shorter_path = parts.slice(0, parts.size - 1).join('/')
|
200
|
+
appendix = parts.last + (tail ? '/' + tail : '')
|
201
|
+
return get_current_translation(shorter_path, appendix)
|
215
202
|
else
|
216
203
|
return path
|
217
204
|
end
|
218
205
|
end
|
219
206
|
|
220
|
-
complete_path = (tail ? translation+
|
207
|
+
complete_path = (tail ? translation + '/' + tail : translation)
|
221
208
|
translated_uri = URI.parse(complete_path)
|
222
209
|
uri.path = translated_uri.path
|
223
210
|
uri.query = [translated_uri.query, uri.query].compact.join('&')
|
@@ -225,10 +212,8 @@ module Rewritten
|
|
225
212
|
uri.to_s
|
226
213
|
end
|
227
214
|
|
228
|
-
|
229
215
|
# infinitive for translations only!
|
230
216
|
def infinitive(some_from)
|
231
|
-
|
232
217
|
some_from ||= ''
|
233
218
|
|
234
219
|
conjugated = some_from.chomp('/')
|
@@ -236,9 +221,9 @@ module Rewritten
|
|
236
221
|
to = translate(conjugated)
|
237
222
|
to = translate(conjugated.split('?')[0]) unless to
|
238
223
|
|
239
|
-
if to.nil? && translate_partial? && conjugated.count('/') > 1
|
224
|
+
if to.nil? && translate_partial? && conjugated.count('/') > 1
|
240
225
|
parts = conjugated.split('/')
|
241
|
-
shorter_path = parts.slice(0, parts.size-1).join('/')
|
226
|
+
shorter_path = parts.slice(0, parts.size - 1).join('/')
|
242
227
|
infinitive(shorter_path)
|
243
228
|
else
|
244
229
|
conjugated = get_current_translation(to) if to
|
@@ -255,23 +240,21 @@ module Rewritten
|
|
255
240
|
base_from
|
256
241
|
elsif translate_partial? && base_from.count('/') > 1
|
257
242
|
parts = base_from.split('/')
|
258
|
-
base_from(parts.slice(0,parts.size-1).join('/'))
|
259
|
-
|
260
|
-
nil
|
261
|
-
end
|
243
|
+
base_from(parts.slice(0, parts.size - 1).join('/'))
|
244
|
+
end
|
262
245
|
end
|
263
246
|
|
264
247
|
def appendix(some_from)
|
265
248
|
base = base_from(some_from) || ''
|
266
|
-
result = some_from.partition(
|
249
|
+
result = some_from.partition(base).last
|
267
250
|
result.chomp('/')
|
268
251
|
end
|
269
252
|
|
270
253
|
def get_flag_string(from)
|
271
|
-
Rewritten.redis.hget("from:#{from}", :flags)||
|
254
|
+
Rewritten.redis.hget("from:#{from}", :flags) || ''
|
272
255
|
end
|
273
256
|
|
274
|
-
def
|
257
|
+
def flag?(from, c)
|
275
258
|
return false unless Rewritten.redis.exists("from:#{from}")
|
276
259
|
get_flag_string(from).index(c) != nil
|
277
260
|
end
|
@@ -279,12 +262,11 @@ module Rewritten
|
|
279
262
|
def full_line(from)
|
280
263
|
flags = get_flag_string(from)
|
281
264
|
|
282
|
-
if flags ==
|
265
|
+
if flags == ''
|
283
266
|
from
|
284
267
|
else
|
285
|
-
"#{from} [#{flags}]"
|
268
|
+
"#{from} [#{flags}]"
|
286
269
|
end
|
287
|
-
|
288
270
|
end
|
289
271
|
|
290
272
|
def exist_translation_for?(path)
|
@@ -292,26 +274,24 @@ module Rewritten
|
|
292
274
|
end
|
293
275
|
|
294
276
|
def add_hit(path, code, content_type)
|
295
|
-
h = {:
|
296
|
-
Rewritten.redis.sadd(
|
277
|
+
h = { path: path, code: code, content_type: content_type }
|
278
|
+
Rewritten.redis.sadd('hits', encode(h))
|
297
279
|
end
|
298
280
|
|
299
281
|
def all_hits
|
300
|
-
Rewritten.redis.smembers(
|
282
|
+
Rewritten.redis.smembers('hits').map { |e| decode(e) }
|
301
283
|
end
|
302
284
|
|
303
285
|
def includes?(path)
|
304
|
-
|
305
286
|
result = Rewritten.redis.hget("from:#{path.chomp('/')}", :to)
|
306
287
|
result = Rewritten.redis.hget("from:#{path.split('?')[0]}", :to) unless result
|
307
288
|
|
308
289
|
if result.nil? && translate_partial? && path.count('/') > 1
|
309
290
|
parts = path.split('/')
|
310
|
-
includes?(
|
291
|
+
includes?(parts.slice(0, parts.size - 1).join('/'))
|
311
292
|
else
|
312
293
|
result
|
313
294
|
end
|
314
|
-
|
315
295
|
end
|
316
296
|
|
317
297
|
# return the number of froms
|
@@ -325,7 +305,7 @@ module Rewritten
|
|
325
305
|
if count == 1
|
326
306
|
redis.zrange(key, start, start)[0]
|
327
307
|
else
|
328
|
-
Array(redis.zrange(key, start, start+count-1)).map do |item|
|
308
|
+
Array(redis.zrange(key, start, start + count - 1)).map do |item|
|
329
309
|
item
|
330
310
|
end
|
331
311
|
end
|
@@ -337,7 +317,7 @@ module Rewritten
|
|
337
317
|
end
|
338
318
|
|
339
319
|
# Returns an array of all known URL targets.
|
340
|
-
def targets
|
320
|
+
def targets
|
341
321
|
Array(redis.smembers(:targets))
|
342
322
|
end
|
343
323
|
|
@@ -353,29 +333,28 @@ module Rewritten
|
|
353
333
|
redis.sadd(:queues, queue.to_s)
|
354
334
|
end
|
355
335
|
|
356
|
-
|
357
336
|
#
|
358
337
|
# stats
|
359
338
|
#
|
360
339
|
|
361
340
|
# Returns a hash, similar to redis-rb's #info, of interesting stats.
|
362
341
|
def info
|
363
|
-
|
364
|
-
:
|
342
|
+
{
|
343
|
+
pending: queues.inject(0) { |m, k| m + size(k) },
|
365
344
|
#:processed => Stat[:processed],
|
366
345
|
#:queues => queues.size,
|
367
346
|
#:workers => workers.size.to_i,
|
368
347
|
#:working => working.size,
|
369
348
|
#:failed => Stat[:failed],
|
370
|
-
:
|
371
|
-
:
|
349
|
+
servers: [redis_id],
|
350
|
+
environment: ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
372
351
|
}
|
373
352
|
end
|
374
353
|
|
375
354
|
# Returns an array of all known Resque keys in Redis. Redis' KEYS operation
|
376
355
|
# is O(N) for the keyspace, so be careful - this can be slow for big databases.
|
377
356
|
def keys
|
378
|
-
redis.keys(
|
357
|
+
redis.keys('*').map do |key|
|
379
358
|
key.sub("#{redis.namespace}:", '')
|
380
359
|
end
|
381
360
|
end
|
@@ -383,7 +362,4 @@ module Rewritten
|
|
383
362
|
def per_page
|
384
363
|
20
|
385
364
|
end
|
386
|
-
|
387
|
-
|
388
365
|
end
|
389
|
-
|