redirectly 0.3.0 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63b305a59fc4d5381391df73f884b91f59c815dc970559828cc5712be4df530f
4
- data.tar.gz: 70bbd12337717b1df14f3ece87f638a0e629e24949b8788b468ca3ab6dbd9387
3
+ metadata.gz: 28d72de69cfc6b42a127abbfdca9c9f7a1cde760d68ad82c266454322d57d7dd
4
+ data.tar.gz: 62e1e4b12baef717c4d1a1da76769e159fd76299d55a3bad926e75680253d72f
5
5
  SHA512:
6
- metadata.gz: 290704128855a3587277ba607737f27701860e9ad0760c1c6f6bfed91c44670ed826deff7cad7a6120f3c3b0b1fcd23f2d620edb88f588e8cbb263524f4d8b0e
7
- data.tar.gz: 8122538c649f2a946ba6b70fbbe5218d8761511c13f7ed0bde8202eb52727bd089650d403588f1d996f9d54bc919c29f4fe6043d1fdf9415713a7c8959b917bc
6
+ metadata.gz: 422b81686d1b73abbf17f0a595598ac0b017c96f03e2c3e63cf934c204c45f2a10e21535c9a234da5e52245b6c0bb53c053f358d937b6f4d833e43080eb6c87a
7
+ data.tar.gz: f39c63692882148bd630ad0fba5972d5a8f0dd8fd2176228f2497c0526491c22e253bcae847616c776c06637db8ce697aedce6b60fdc188a1b8c86025396673c
data/README.md CHANGED
@@ -95,6 +95,8 @@ example.com = https://other-site.com/
95
95
  example.org/* = https://other-site.com/
96
96
  *.old-site.com = !https://permanent.redirect.com
97
97
  :sub.app.localhost/* = http://it-works.com/%{sub}
98
+ proxy.localhost/*rest = @https://proxy.target.com/base/*rest
99
+ internal.localhost/reload = :reload
98
100
  (*)old-domain.com/*rest = http://new-domain.com/%{rest}
99
101
  ```
100
102
 
@@ -109,14 +111,44 @@ The configuration file is built of `pattern = target` pairs, where:
109
111
  - `pattern` - is any URL pattern that is supported by [Mustermann][mustermann].
110
112
  - `target` - is the target URL to redirect to.
111
113
 
112
- Notes:
114
+ ### Special INI Notation
113
115
 
114
- - If `target` starts with an exclamation mark, it will be a permanent
115
- redirect (301), otherwise it will be a temporary redirect (302).
116
- - If `pattern` includes named arguments (e.g. `example.com/:something`), they
117
- will be available to the `target` as Ruby string substitution variables
118
- (e.g. `%{something}`).
116
+ #### Redirect type
119
117
 
118
+ If the target starts with `!`, a permanent redirect (301) will be performed.
119
+ If it does not, a temporary redirect (302) will be performed by default:
120
+
121
+ ```ini
122
+ test.localhost/temporary = http://example.com
123
+ test.localhost/permanent = !http://example.com
124
+ ```
125
+
126
+ #### Proxying
127
+
128
+ If the target starts with `@`, the content will be proxied instead of being
129
+ redirected:
130
+
131
+ ```ini
132
+ test.localhost = @http://example.com
133
+ ```
134
+
135
+ #### Named arguments
136
+
137
+ Patterns that include strings starting with a colon `:` will expose those
138
+ strings as Ruby substitution variables in the target:
139
+
140
+ ```ini
141
+ test.localhost/:anything = http://example.com/path/%{anything}
142
+ ```
143
+
144
+ #### Splats
145
+
146
+ You can use a splat `*` or a named splat `*name` in the pattern.
147
+ Named splats will also be exposed as Ruby substitution variables in the target:
148
+
149
+ ```ini
150
+ (*)test.localhost/*rest = http://example.com/%{rest}
151
+ ```
120
152
 
121
153
  ## Contributing / Support
122
154
 
@@ -1,6 +1,8 @@
1
1
  require 'cgi'
2
- require 'rack'
3
2
  require 'mustermann'
3
+ require 'net/http'
4
+ require 'rack'
5
+ require 'uri'
4
6
 
5
7
  module Redirectly
6
8
  class App
@@ -14,10 +16,10 @@ module Redirectly
14
16
 
15
17
  def call(env)
16
18
  @req = Rack::Request.new env
17
- found = match
19
+ found = find_match
18
20
 
19
21
  if found
20
- redirect_to found
22
+ handle_target found
21
23
  else
22
24
  not_found
23
25
  end
@@ -25,6 +27,16 @@ module Redirectly
25
27
 
26
28
  private
27
29
 
30
+ def handle_target(target)
31
+ if target.start_with? ':'
32
+ run_internal_command target[1..]
33
+ elsif target.start_with? '@'
34
+ proxy_to target[1..]
35
+ else
36
+ redirect_to target
37
+ end
38
+ end
39
+
28
40
  def redirect_to(target)
29
41
  code = 302
30
42
 
@@ -36,20 +48,48 @@ module Redirectly
36
48
  [code, { 'location' => target }, []]
37
49
  end
38
50
 
51
+ def proxy_to(target)
52
+ uri = URI target
53
+ uri.query = req.query_string unless req.query_string.empty?
54
+
55
+ response = Net::HTTP.get_response uri
56
+
57
+ headers = { 'Content-Type' => response['Content-Type'] }
58
+ [response.code.to_i, headers, [response.body]]
59
+ rescue => e
60
+ [502, { 'Content-Type' => 'text/plain' }, ["Bad Gateway: #{e.message}"]]
61
+ end
62
+
63
+ def run_internal_command(target)
64
+ case target.to_sym
65
+ when :reload then reload_ini
66
+ else not_found
67
+ end
68
+ end
69
+
39
70
  def not_found
40
71
  [404, { 'content-type' => 'text/plain' }, ['Not Found']]
41
72
  end
42
73
 
43
74
  def redirects
44
- @redirects ||= ini_read(config_path)
75
+ if ENV['REDIRECTLY_RELOAD']
76
+ ini_read config_path
77
+ else
78
+ @redirects ||= ini_read(config_path)
79
+ end
80
+ end
81
+
82
+ def reload_ini
83
+ @redirects = nil
84
+ [200, { 'content-type' => 'text/plain' }, ['OK (:reload)']]
45
85
  end
46
86
 
47
87
  def ini_read(path)
48
- content = File.readlines(path, chomp: true).reject(&:comment?).reject(&:empty?)
88
+ content = File.readlines(path, chomp: true).reject(&:ignored?)
49
89
  content.to_h { |line| line.split(/\s*=\s*/, 2) }
50
90
  end
51
91
 
52
- def match
92
+ def find_match
53
93
  redirects.each do |pattern, target|
54
94
  found = find_target pattern, target
55
95
  return found if found
@@ -8,17 +8,21 @@ module Redirectly
8
8
  help 'Start the redirect server'
9
9
  version Redirectly::VERSION
10
10
 
11
- usage 'redirectly [CONFIG --port PORT]'
11
+ usage 'redirectly [CONFIG --port PORT --reload]'
12
12
  usage 'redirectly --init'
13
- usage 'redirectly --help | --version'
13
+ usage 'redirectly -h | --help | --version'
14
14
 
15
15
  option '-p --port PORT', 'Listening port [default: 3000]'
16
16
  option '-i --init', 'Create a sample config file and exit'
17
+ option '-r --reload', 'Read the INI file with every request'
17
18
 
18
19
  param 'CONFIG', 'Path to config file [default: redirects.ini]'
19
20
 
21
+ environment 'REDIRECTLY_RELOAD',
22
+ 'Set to a non empty value in order to read the INI file with every request (same as --reload)'
23
+
20
24
  example 'redirectly --init'
21
- example 'redirectly config.ini'
25
+ example 'redirectly config.ini --port 4000 --reload'
22
26
 
23
27
  attr_reader :config_path, :port
24
28
 
@@ -44,6 +48,8 @@ module Redirectly
44
48
  example.org/* = https://other-site.com/
45
49
  *.old-site.com = !https://permanent.redirect.com
46
50
  :sub.app.localhost/* = http://it-works.com/%{sub}
51
+ proxy.localhost/*rest = @https://proxy.target.com/base/*rest
52
+ internal.localhost/reload = :reload
47
53
  (*)old-domain.com/*rest = http://new-domain.com/%{rest}
48
54
  TEMPLATE
49
55
  end
@@ -51,6 +57,7 @@ module Redirectly
51
57
  def start_server
52
58
  raise ArgumentError, "Cannot find config file #{config_path}" unless File.exist? config_path
53
59
 
60
+ ENV['REDIRECTLY_RELOAD'] = '1' if args['--reload']
54
61
  Rackup::Server.start(app: app, Port: port, environment: 'production')
55
62
  end
56
63
 
@@ -1,8 +1,16 @@
1
1
  module Redirectly
2
2
  module Refinements
3
3
  refine String do
4
+ def ignored?
5
+ empty? || comment? || section?
6
+ end
7
+
4
8
  def comment?
5
- start_with? ';' or start_with? '#'
9
+ start_with?(';') || start_with?('#')
10
+ end
11
+
12
+ def section?
13
+ match?(/^\[.+\]\s*$/)
6
14
  end
7
15
  end
8
16
  end
@@ -1,3 +1,3 @@
1
1
  module Redirectly
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redirectly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danny Ben Shitrit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-13 00:00:00.000000000 Z
11
+ date: 2024-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mister_bin
@@ -122,14 +122,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
122
122
  requirements:
123
123
  - - ">="
124
124
  - !ruby/object:Gem::Version
125
- version: '3.0'
125
+ version: '3.1'
126
126
  required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  requirements:
128
128
  - - ">="
129
129
  - !ruby/object:Gem::Version
130
130
  version: '0'
131
131
  requirements: []
132
- rubygems_version: 3.5.6
132
+ rubygems_version: 3.5.23
133
133
  signing_key:
134
134
  specification_version: 4
135
135
  summary: Redirectly redirect server