rrproxy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|