rrproxy 0.0.1
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.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +50 -0
- data/Rakefile +2 -0
- data/bin/rrproxy +68 -0
- data/example/example_config.rb +8 -0
- data/lib/rrproxy.rb +54 -0
- data/rrproxy.gemspec +21 -0
- metadata +121 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Caleb Spare
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
rrproxy
|
2
|
+
=======
|
3
|
+
|
4
|
+
rrproxy ("routing reverse proxy" or "rockin' ruby proxy" -- you pick) is a very simple reverse proxy server
|
5
|
+
that can route requests to different servers based on simple pattern-matching rules against the requests. It
|
6
|
+
is intended for use in a development environment as a substitute for 'real' proxies (nginx/HAProxy/whatever)
|
7
|
+
that you use in production.
|
8
|
+
|
9
|
+
Installation
|
10
|
+
------------
|
11
|
+
|
12
|
+
$ gem install rrproxy
|
13
|
+
|
14
|
+
rrproxy requires Ruby 1.9 (because it's based on Goliath).
|
15
|
+
|
16
|
+
Usage
|
17
|
+
-----
|
18
|
+
|
19
|
+
rrproxy takes a very simple Ruby configuration file.
|
20
|
+
|
21
|
+
[
|
22
|
+
["/styles/screen.css", "localhost:8000"],
|
23
|
+
["", "example.com"]
|
24
|
+
]
|
25
|
+
|
26
|
+
As you can probably guess, this will redirect requests for example.com's css file to a local server (while
|
27
|
+
passing all other requests through normally).
|
28
|
+
|
29
|
+
Here's a more complicated example (it's also in `examples/example_config.rb`):
|
30
|
+
|
31
|
+
[
|
32
|
+
["/foo/bar?special=true", "localhost:1234"], # Route this specific request to a local server
|
33
|
+
[%r{^(/foo).*}, "google.com", ""], # You can use a regex, and also replace the match with a third arg
|
34
|
+
["", "amazon.com"] # Empty string matches everything
|
35
|
+
]
|
36
|
+
|
37
|
+
You will provide an array of tuples, where each one is of the form
|
38
|
+
|
39
|
+
[pattern, target_host, <optional replacement string>]
|
40
|
+
|
41
|
+
For each request, rrproxy will examine each routing rule in order, checking for a prefix match (if the pattern
|
42
|
+
is a string) or a regex match. The first match found will be used, and the host for the request will be
|
43
|
+
changed to the corresponding target host. If a replacement is provided, it will be used as a substitute for
|
44
|
+
the first regex matching group (if the pattern is a regex) or the whole pattern (if the pattern is a string).
|
45
|
+
|
46
|
+
Credits
|
47
|
+
-------
|
48
|
+
|
49
|
+
* @tobert for the idea to use Goliath
|
50
|
+
* The PostRank folks for a kickass server framework
|
data/Rakefile
ADDED
data/bin/rrproxy
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "trollop"
|
4
|
+
require "dedent"
|
5
|
+
require "colorize"
|
6
|
+
|
7
|
+
require "rrproxy"
|
8
|
+
|
9
|
+
# The goliath folks really need to take a page out of Sinatra's book and provide an easy way to start up a
|
10
|
+
# server manually without using all their application runner and option parsing shit (in Sinatra terms,
|
11
|
+
# 'sinatra' vs 'sinatra/base'). As it is I had to poke around the source code a while to figure out all the
|
12
|
+
# little things necessary to make it work.
|
13
|
+
|
14
|
+
# See https://github.com/postrank-labs/goliath/blob/master/lib/goliath/runner.rb
|
15
|
+
def create_logger
|
16
|
+
logger = Log4r::Logger.new("rrproxy")
|
17
|
+
log_format = Log4r::PatternFormatter.new(:pattern => "[%d : %l] %m")
|
18
|
+
logger.add(Log4r::StdoutOutputter.new("console", :formatter => log_format))
|
19
|
+
logger.level = Log4r::INFO
|
20
|
+
logger
|
21
|
+
end
|
22
|
+
|
23
|
+
options = Trollop.options do
|
24
|
+
banner <<-EOS.dedent
|
25
|
+
rrproxy is a very simple reverse http proxy that can route requests based on pattern-matching the
|
26
|
+
URI. The configuration file is Ruby, and it must have the following form:
|
27
|
+
|
28
|
+
[
|
29
|
+
["/foo/bar?special=true", "localhost:1234"], # Route this specific request to a local server
|
30
|
+
[%r{^(/foo).*}, "google.com", ""], # You can use a regex, and also replace the match
|
31
|
+
["", "amazon.com"] # Empty string matches everything
|
32
|
+
]
|
33
|
+
|
34
|
+
Usage:
|
35
|
+
|
36
|
+
$ rrproxy [options] CONFIG_FILE
|
37
|
+
|
38
|
+
where [options] are:
|
39
|
+
EOS
|
40
|
+
opt :address, "Bind address", :default => "0.0.0.0"
|
41
|
+
opt :port, "Port", :default => 9876
|
42
|
+
end
|
43
|
+
|
44
|
+
Trollop.die "You must provide a configuration file" unless ARGV[0]
|
45
|
+
Trollop.die "Invalid config file: #{ARGV[0]}" unless File.file? ARGV[0]
|
46
|
+
begin
|
47
|
+
RRProxy.routes = eval(File.read(ARGV[0]))
|
48
|
+
rescue
|
49
|
+
Trollop.die "Error reading or parsing the config file #{ARGV[0]}"
|
50
|
+
end
|
51
|
+
|
52
|
+
logger = create_logger
|
53
|
+
logger.info "Starting rrproxy (powered by Goliath) on #{options[:address]}:#{options[:port]}".green
|
54
|
+
RRProxy.routes.each do |route|
|
55
|
+
pattern, target, replacement = route
|
56
|
+
message = "Proxying `#{pattern}` to #{target}"
|
57
|
+
message << " and replacing pattern matches with `#{replacement}`" if replacement
|
58
|
+
logger.info message.green
|
59
|
+
end
|
60
|
+
|
61
|
+
Goliath.env = :development
|
62
|
+
api = RRProxy.new
|
63
|
+
server = Goliath::Server.new(options[:address], options[:port])
|
64
|
+
server.logger = logger
|
65
|
+
server.api = api
|
66
|
+
server.app = Goliath::Rack::Builder.build(RRProxy, api)
|
67
|
+
server.plugins = []
|
68
|
+
server.start
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# General form: An array of
|
2
|
+
# [pattern, target_host, <optional replacement string>]
|
3
|
+
|
4
|
+
[
|
5
|
+
["/foo/bar?special=true", "localhost:1234"], # Route this specific request to a local server
|
6
|
+
[%r{^(/foo).*}, "google.com", ""], # You can use a regex, and also replace the match with a third arg
|
7
|
+
["", "amazon.com"] # Empty string matches everything
|
8
|
+
]
|
data/lib/rrproxy.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require "goliath/api"
|
2
|
+
require "goliath/goliath"
|
3
|
+
require "goliath/rack"
|
4
|
+
require "goliath/server"
|
5
|
+
require "log4r"
|
6
|
+
require "em-synchrony/em-http"
|
7
|
+
|
8
|
+
class RRProxy < Goliath::API
|
9
|
+
class << self
|
10
|
+
attr_accessor :routes
|
11
|
+
end
|
12
|
+
|
13
|
+
use Goliath::Rack::Params
|
14
|
+
|
15
|
+
def options_parser(option_parser, options)
|
16
|
+
option_parser.on("-c", "--config CONFIG_FILE", "Ruby routes configuration file") do |filename|
|
17
|
+
options[:config] = filename
|
18
|
+
end
|
19
|
+
abort "asdf" unless File.file?(options[:config])
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_headers(env, headers) env["untampered_headers"] = headers end
|
23
|
+
|
24
|
+
def response(env)
|
25
|
+
uri = env["REQUEST_URI"]
|
26
|
+
method = env["REQUEST_METHOD"]
|
27
|
+
logger.info "Proxying #{method} #{uri}"
|
28
|
+
method = method.downcase.to_sym
|
29
|
+
RRProxy.routes.each do |pattern, target, replacement|
|
30
|
+
if pattern.is_a?(Regexp)
|
31
|
+
next unless uri =~ pattern
|
32
|
+
uri.sub!($1, replacement) if replacement
|
33
|
+
else
|
34
|
+
next unless uri.start_with? pattern
|
35
|
+
uri.sub!(pattern, replacement) if replacement
|
36
|
+
end
|
37
|
+
|
38
|
+
target_url = "http://#{target}#{uri}"
|
39
|
+
headers = env["untampered_headers"]
|
40
|
+
headers["HOST"] = target
|
41
|
+
params = { :head => headers }
|
42
|
+
params[:body] = env["params"] if [:put, :post, :patch].include? method
|
43
|
+
|
44
|
+
http = EM::HttpRequest.new(target_url).send(method, params)
|
45
|
+
response_headers = {}
|
46
|
+
http.response_header.each do |k, v|
|
47
|
+
response_headers[k.downcase.split("_").map(&:capitalize).join("-")] = v
|
48
|
+
end
|
49
|
+
|
50
|
+
return [http.response_header.status, response_headers, [http.response]]
|
51
|
+
end
|
52
|
+
[503, {}, "No target specified for this path."]
|
53
|
+
end
|
54
|
+
end
|
data/rrproxy.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.authors = ["Caleb Spare"]
|
3
|
+
gem.email = ["cespare@gmail.com"]
|
4
|
+
gem.description = %q{rrproxy is a very simple routing reverse proxy written in Ruby.}
|
5
|
+
gem.summary = %q{A routing reverse proxy.}
|
6
|
+
gem.homepage = ""
|
7
|
+
|
8
|
+
gem.files = `git ls-files`.split($\)
|
9
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
10
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
11
|
+
gem.name = "rrproxy"
|
12
|
+
gem.version = "0.0.1"
|
13
|
+
gem.require_paths = ["lib"]
|
14
|
+
|
15
|
+
gem.add_dependency "goliath"
|
16
|
+
gem.add_dependency "log4r"
|
17
|
+
gem.add_dependency "em-http-request"
|
18
|
+
gem.add_dependency "trollop"
|
19
|
+
gem.add_dependency "dedent"
|
20
|
+
gem.add_dependency "colorize"
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rrproxy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Caleb Spare
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-16 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: goliath
|
16
|
+
requirement: &24235400 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *24235400
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: log4r
|
27
|
+
requirement: &24200780 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *24200780
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: em-http-request
|
38
|
+
requirement: &24199760 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *24199760
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: trollop
|
49
|
+
requirement: &24197520 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *24197520
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: dedent
|
60
|
+
requirement: &24196100 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *24196100
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: colorize
|
71
|
+
requirement: &24194600 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *24194600
|
80
|
+
description: rrproxy is a very simple routing reverse proxy written in Ruby.
|
81
|
+
email:
|
82
|
+
- cespare@gmail.com
|
83
|
+
executables:
|
84
|
+
- rrproxy
|
85
|
+
extensions: []
|
86
|
+
extra_rdoc_files: []
|
87
|
+
files:
|
88
|
+
- .gitignore
|
89
|
+
- Gemfile
|
90
|
+
- LICENSE
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- bin/rrproxy
|
94
|
+
- example/example_config.rb
|
95
|
+
- lib/rrproxy.rb
|
96
|
+
- rrproxy.gemspec
|
97
|
+
homepage: ''
|
98
|
+
licenses: []
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 1.8.10
|
118
|
+
signing_key:
|
119
|
+
specification_version: 3
|
120
|
+
summary: A routing reverse proxy.
|
121
|
+
test_files: []
|