path_router 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +22 -0
- data/README.md +67 -0
- data/Rakefile +1 -0
- data/bin/path_router +37 -0
- data/example_path_router.rb +17 -0
- data/lib/path_router/router.rb +46 -0
- data/lib/path_router/version.rb +3 -0
- data/lib/path_router.rb +2 -0
- data/path_router.gemspec +23 -0
- data/proxymachine_config.rb +18 -0
- data/spec/path_router_spec.rb +36 -0
- data/spec/spec_helper.rb +3 -0
- metadata +86 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format documentation
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
eventmachine (0.12.10)
|
6
|
+
proxymachine (1.2.4)
|
7
|
+
eventmachine (>= 0.12.10)
|
8
|
+
rspec (2.6.0)
|
9
|
+
rspec-core (~> 2.6.0)
|
10
|
+
rspec-expectations (~> 2.6.0)
|
11
|
+
rspec-mocks (~> 2.6.0)
|
12
|
+
rspec-core (2.6.4)
|
13
|
+
rspec-expectations (2.6.0)
|
14
|
+
diff-lcs (~> 1.1.2)
|
15
|
+
rspec-mocks (2.6.0)
|
16
|
+
|
17
|
+
PLATFORMS
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
proxymachine
|
22
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
path_router
|
2
|
+
===========
|
3
|
+
|
4
|
+
HTTP routing by URL path, built on the excellent [ProxyMachine](https://github.com/mojombo/proxymachine) by the GitHub guys.
|
5
|
+
|
6
|
+
GitHub uses ProxyMachine to service millions of git connections every day.
|
7
|
+
|
8
|
+
|
9
|
+
Install
|
10
|
+
-------
|
11
|
+
|
12
|
+
```
|
13
|
+
gem install path_router # installs path_router and proxymachine
|
14
|
+
path_router init # creates example path_router.rb
|
15
|
+
vim path_router.rb # specify your backends and URL patterns
|
16
|
+
path_router # fire it up, optionally with -h host and -p port
|
17
|
+
```
|
18
|
+
|
19
|
+
Routes
|
20
|
+
------
|
21
|
+
|
22
|
+
See `example_path_router.rb` to get the general idea.
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
PathRouter::Router.instance.reset.tap do |r|
|
26
|
+
|
27
|
+
r.backends = {
|
28
|
+
rails: "127.0.0.1:3000", # default
|
29
|
+
static: "127.0.0.1:8080",
|
30
|
+
wordpress: "10.0.0.1:80",
|
31
|
+
}
|
32
|
+
|
33
|
+
r.connect :static,
|
34
|
+
%r{^/robots.txt},
|
35
|
+
%r{^/(?:images|stylesheets|javascripts)/}
|
36
|
+
|
37
|
+
r.connect :wordpress,
|
38
|
+
%r{^/blog/},
|
39
|
+
%r{^/feed}
|
40
|
+
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
The only non-obvious point is that the first backend in the hash is the default catch-all.
|
45
|
+
Ruby 1.8 users get random default backend; think of it as an easter egg ;)
|
46
|
+
|
47
|
+
|
48
|
+
Tests
|
49
|
+
-----
|
50
|
+
|
51
|
+
```
|
52
|
+
$ rspec spec/*_spec.rb
|
53
|
+
|
54
|
+
PathRouter::Router
|
55
|
+
routes by specified patterns
|
56
|
+
defaults to first backend for unmatched requests
|
57
|
+
|
58
|
+
Finished in 0.00056 seconds
|
59
|
+
2 examples, 0 failures
|
60
|
+
```
|
61
|
+
|
62
|
+
|
63
|
+
Meh.
|
64
|
+
----
|
65
|
+
|
66
|
+
(c) 2011 Paul Annesley, MIT licence.
|
67
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/bin/path_router
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
BASEDIR = File.realpath(File.join(File.dirname(__FILE__), ".."))
|
6
|
+
|
7
|
+
if ARGV.include? "init"
|
8
|
+
require "fileutils"
|
9
|
+
dest = "./path_router.rb"
|
10
|
+
FileUtils.cp File.join(BASEDIR, "example_path_router.rb"), dest
|
11
|
+
puts "Created example #{dest}"
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
|
15
|
+
args = [ "--config", File.join(BASEDIR, "proxymachine_config.rb") ]
|
16
|
+
|
17
|
+
OptionParser.new do |opts|
|
18
|
+
opts.banner = <<-EOF
|
19
|
+
|
20
|
+
path_router init
|
21
|
+
Creates example path_router.rb into the current directory.
|
22
|
+
|
23
|
+
path_router [-h <host>] [-p <port>]
|
24
|
+
Start ProxyMachine with path_router config loaded from path_router.rb in
|
25
|
+
the current directory.
|
26
|
+
|
27
|
+
Options:
|
28
|
+
EOF
|
29
|
+
opts.on("-hHOST", "--host HOST", "Hostname to bind. Default 0.0.0.0") do |host|
|
30
|
+
args += [ "--host", host ]
|
31
|
+
end
|
32
|
+
opts.on("-pPORT", "--port PORT", "Port to listen on. Default 5432") do |port|
|
33
|
+
args += [ "--port", port ]
|
34
|
+
end
|
35
|
+
end.parse!
|
36
|
+
|
37
|
+
exec "proxymachine", *args
|
@@ -0,0 +1,17 @@
|
|
1
|
+
PathRouter::Router.instance.reset.tap do |r|
|
2
|
+
|
3
|
+
r.backends = {
|
4
|
+
rails: "127.0.0.1:3000", # default
|
5
|
+
static: "127.0.0.1:8080",
|
6
|
+
wordpress: "10.0.0.1:80",
|
7
|
+
}
|
8
|
+
|
9
|
+
r.connect :static,
|
10
|
+
%r{^/robots.txt},
|
11
|
+
%r{^/(?:images|stylesheets|javascripts)/}
|
12
|
+
|
13
|
+
r.connect :wordpress,
|
14
|
+
%r{^/blog/},
|
15
|
+
%r{^/feed}
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module PathRouter
|
2
|
+
class Router
|
3
|
+
|
4
|
+
def self.instance
|
5
|
+
@instance ||= new
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
reset
|
10
|
+
end
|
11
|
+
|
12
|
+
def reset
|
13
|
+
@backends = {}
|
14
|
+
@routes = {}
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def load_routes(file = "path_router.rb")
|
19
|
+
puts "Loading routes from #{file}"
|
20
|
+
load file
|
21
|
+
rescue LoadError => e
|
22
|
+
puts "Failed to load routes: " << e.message
|
23
|
+
end
|
24
|
+
|
25
|
+
# Hash of backends, { Regexp => "ip:port", ... }
|
26
|
+
def backends=(backends)
|
27
|
+
backends.default = backends.values.first
|
28
|
+
@backends = backends
|
29
|
+
end
|
30
|
+
|
31
|
+
# Connect patterns to the given backend.
|
32
|
+
def connect(backend, *patterns)
|
33
|
+
patterns.each { |p| @routes[p] = backend }
|
34
|
+
end
|
35
|
+
|
36
|
+
# method e.g. "GET", currently unused
|
37
|
+
# path e.g. "/some/path?including=query+string"
|
38
|
+
def lookup(method, path)
|
39
|
+
_, backend = @routes.detect do |pattern, _|
|
40
|
+
path =~ pattern
|
41
|
+
end
|
42
|
+
@backends[backend]
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
data/lib/path_router.rb
ADDED
data/path_router.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "path_router/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "path_router"
|
7
|
+
s.version = PathRouter::VERSION
|
8
|
+
s.authors = ["Paul Annesley"]
|
9
|
+
s.email = ["paul@annesley.cc"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{HTTP routing by URL path, built on ProxyMachine.}
|
12
|
+
s.description = %q{Easily route HTTP requests to different backends by pattern matching the URL path.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "path_router"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "proxymachine", "~> 1.2.4"
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
$LOAD_PATH.unshift "./lib"
|
2
|
+
require "path_router/router"
|
3
|
+
|
4
|
+
module PathRouter
|
5
|
+
|
6
|
+
Signal.trap("USR2") { Router.instance.load_routes }
|
7
|
+
|
8
|
+
Router.instance.load_routes
|
9
|
+
|
10
|
+
proxy do |data|
|
11
|
+
if data =~ %r{^(\w+) (\S+) HTTP/\d.\d\r\n}
|
12
|
+
{ remote: Router.instance.lookup($1, $2) }
|
13
|
+
else
|
14
|
+
{ noop: true }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
module PathRouter
|
4
|
+
describe Router do
|
5
|
+
|
6
|
+
let(:router) do
|
7
|
+
Router.new.tap do |r|
|
8
|
+
|
9
|
+
r.backends = {
|
10
|
+
rails: "127.0.0.1:3000",
|
11
|
+
static: "127.0.0.1:8080",
|
12
|
+
api: "api:80"
|
13
|
+
}
|
14
|
+
|
15
|
+
r.connect :static,
|
16
|
+
%r{^/robots.txt},
|
17
|
+
%r{^/(?:images|stylesheets|javascripts)/}
|
18
|
+
|
19
|
+
r.connect :api,
|
20
|
+
%r{^/api/}
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "routes by specified patterns" do
|
26
|
+
router.lookup("GET", "/robots.txt").should == "127.0.0.1:8080"
|
27
|
+
router.lookup("GET", "/images/logo.png").should == "127.0.0.1:8080"
|
28
|
+
router.lookup("GET", "/api/list").should == "api:80"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "defaults to first backend for unmatched requests" do
|
32
|
+
router.lookup("GET", "/anything/else").should == "127.0.0.1:3000"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: path_router
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Annesley
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-06-17 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: proxymachine
|
16
|
+
requirement: &2169316300 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.2.4
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2169316300
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &2169315880 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2169315880
|
36
|
+
description: Easily route HTTP requests to different backends by pattern matching
|
37
|
+
the URL path.
|
38
|
+
email:
|
39
|
+
- paul@annesley.cc
|
40
|
+
executables:
|
41
|
+
- path_router
|
42
|
+
extensions: []
|
43
|
+
extra_rdoc_files: []
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- .rspec
|
47
|
+
- Gemfile
|
48
|
+
- Gemfile.lock
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- bin/path_router
|
52
|
+
- example_path_router.rb
|
53
|
+
- lib/path_router.rb
|
54
|
+
- lib/path_router/router.rb
|
55
|
+
- lib/path_router/version.rb
|
56
|
+
- path_router.gemspec
|
57
|
+
- proxymachine_config.rb
|
58
|
+
- spec/path_router_spec.rb
|
59
|
+
- spec/spec_helper.rb
|
60
|
+
homepage: ''
|
61
|
+
licenses: []
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project: path_router
|
80
|
+
rubygems_version: 1.8.5
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: HTTP routing by URL path, built on ProxyMachine.
|
84
|
+
test_files:
|
85
|
+
- spec/path_router_spec.rb
|
86
|
+
- spec/spec_helper.rb
|