proxyreverse 0.1.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/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in proxyreverse.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Andy Thompson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,3 @@
1
+ Manifest
2
+ Rakefile
3
+ bin/proxyreverse
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # ProxyReverse
2
+
3
+ ProxyReverse proxies your local web-server and makes it act as if
4
+ coming from your local host, rewriting the response, so that links
5
+ stay inside the proxy domain
6
+
7
+ ## Installation
8
+
9
+ ProxyReverse is a tool that runs on the command line.
10
+
11
+ On any system with [ruby] and [rubygems] installed, open your terminal
12
+ and type:
13
+
14
+ $ gem install proxyreverse
15
+
16
+ ## Usage
17
+
18
+ Assuming that you are running your local web-server on a VM with a host-only
19
+ interface with local access via http://my.dev/
20
+
21
+ $ proxyreverse 8080 my.dev
22
+ http://my.dev is now publicly available via:
23
+ http://localhost:8080/
24
+
25
+ Now you can open this link in your favorite browser and request will
26
+ be proxied to your local VM.
27
+
28
+ [ruby]: http://www.ruby-lang.org/en/downloads/
29
+ [rubygems]: https://rubygems.org/pages/download
30
+ [github]: https://github.com/andytson/proxyremote
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/proxyreverse ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'proxyreverse/command'
5
+ rescue LoadError
6
+ proxyreverse_path = File.expand_path('../../lib', __FILE__)
7
+ $:.unshift(proxyreverse_path) if File.directory?(proxyreverse_path) && !$:.include?(proxyreverse_path)
8
+ require 'proxyreverse/command'
9
+ end
10
+
@@ -0,0 +1,18 @@
1
+ require 'proxyreverse/version'
2
+
3
+ module ProxyReverse
4
+ autoload :Client, 'proxyreverse/client'
5
+ autoload :FrontendRequest, 'proxyreverse/frontendrequest'
6
+ autoload :BackendResponse, 'proxyreverse/backendresponse'
7
+ autoload :Command, 'proxyreverse/command'
8
+
9
+ class << self
10
+ def logger
11
+ @@logger ||= nil
12
+ end
13
+
14
+ def logger=(logger)
15
+ @@logger = logger
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,89 @@
1
+ module ProxyReverse
2
+ class BackendResponse
3
+ def initialize(client, request)
4
+ @parser = HTTP::ResponseParser.new(self)
5
+ @client = client
6
+ @request = request
7
+ end
8
+
9
+ def receive_data(data)
10
+ @parser << data
11
+ end
12
+
13
+ def on_message_begin
14
+ @headers = nil
15
+ @body = ''
16
+ @transferEncoding = 'identity'
17
+ @contentEncoding = 'identity'
18
+ @complete = false
19
+ end
20
+
21
+ def on_headers_complete(env)
22
+ @headers = @parser.headers
23
+ @host = @headers['Host']
24
+ @transferEncoding = 'identity'
25
+ @contentEncoding = 'identity'
26
+
27
+ if @client.options[:rewrite_domain] && @headers.has_key?('Location')
28
+ @headers['Location'] = @headers['Location'].sub(/^#{@client.options['regex']}/, "\\1#{@request.host}")
29
+ end
30
+
31
+ if @headers.has_key?('Content-Encoding')
32
+ @contentEncoding = @headers['Content-Encoding']
33
+ end
34
+
35
+ if @headers.has_key?('Transfer-Encoding')
36
+ @transferEncoding = @headers['Transfer-Encoding']
37
+ end
38
+ @headers['Transfer-Encoding'] = 'chunked'
39
+ @headers.delete('Content-Length')
40
+
41
+ buf = "HTTP/#{@parser.http_version.join('.')} #{@parser.status_code} Message missing\r\n"
42
+ @headers.each_pair do |name, value|
43
+ buf << "#{name}: #{value}\r\n"
44
+ end
45
+ buf << "\r\n"
46
+
47
+ @client.send_data(buf)
48
+ end
49
+
50
+ def on_body(chunk)
51
+ @body << chunk
52
+ end
53
+
54
+ def on_message_complete
55
+ case @contentEncoding
56
+ when 'gzip'
57
+ reader = Zlib::GzipReader.new(StringIO.new(@body))
58
+ new_response_body = reader.read
59
+ reader.close
60
+ @body = new_response_body
61
+ when 'deflate'
62
+ @body = Zlib::Inflate.inflate(@body)
63
+ end
64
+ @body = @body.gsub(/#{@client.options['regex']}/, "\\1#{@request.host}")
65
+
66
+ case @contentEncoding
67
+ when 'gzip'
68
+ new_response_body = ''
69
+ writer = Zlib::GzipWriter.new(StringIO.new(new_response_body))
70
+ writer.write(@body)
71
+ writer.close
72
+ @body = new_response_body
73
+ when 'deflate'
74
+ @body = Zlib::Deflate.deflate(@body)
75
+ end
76
+
77
+ if @body.length > 0
78
+ @client.send_data "#{@body.length.to_s(16)}\r\n#{@body}\r\n0\r\n\r\n"
79
+ else
80
+ @client.send_data "0\r\n\r\n"
81
+ end
82
+ @complete = true
83
+ end
84
+
85
+ def complete?
86
+ @complete
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,84 @@
1
+ require 'logger'
2
+ require 'em-proxy'
3
+ require 'http/parser'
4
+ require 'zlib'
5
+
6
+ module ProxyReverse
7
+ class Client < EventMachine::ProxyServer::Connection
8
+ attr_accessor :options
9
+
10
+ def self.run(options = {})
11
+ @@logger = Logger.new(STDOUT)
12
+ @@logger.level = options[:verbose] ? Logger::INFO : Logger::WARN
13
+
14
+ @@logger.info("Run with options #{options.inspect}")
15
+
16
+ begin
17
+ trap 'SIGCLD', 'IGNORE'
18
+ trap 'INT' do
19
+ puts
20
+ EventMachine.stop
21
+ exit
22
+ end
23
+ rescue ArgumentError
24
+ end
25
+
26
+ EventMachine.epoll
27
+ EventMachine.run { connect(options) }
28
+ end
29
+
30
+ def initialize(options)
31
+ super(:debug => false)
32
+ @options = options
33
+ @responses = {}
34
+ transferEncoding = 'identity'
35
+
36
+ if @options[:rewrite_domain] == :subdomains
37
+ @options[:rewrite_domain] = "." + @options[:backend_host].sub(/^www./, '')
38
+ end
39
+
40
+ if @options[:rewrite_domain][0] == '.'
41
+ @options['regex'] = "(https?:\\/\\/)([a-z\.]+\\.)?#{Regexp.escape(@options[:rewrite_domain][1..-1])}"
42
+ else
43
+ @options['regex'] = "(https?:\\/\\/)#{Regexp.escape(@options[:rewrite_domain])}"
44
+ end
45
+
46
+ server :srv, :host => @options[:backend_host], :port => @options[:backend_port]
47
+ end
48
+
49
+ def self.connect(options)
50
+ EventMachine::start_server(options[:host], options[:port], self, options)
51
+ end
52
+
53
+ def connected(name)
54
+ end
55
+
56
+ def relay_from_backend(name, data)
57
+ begin
58
+ while data.length > 0
59
+ if @responses[name].nil? || @responses[name].complete?
60
+ @responses[name] = ProxyReverse::BackendResponse.new(self, @request)
61
+ end
62
+ offset = @responses[name].receive_data(data)
63
+ data = data[offset..-1]
64
+ end
65
+ rescue HTTP::Parser::Error => e
66
+ raise e if e.message != 'Could not parse data entirely'
67
+ end
68
+ end
69
+
70
+ def receive_data(data)
71
+ begin
72
+ while data.length > 0
73
+ if @request.nil? || @request.complete?
74
+ @request = ProxyReverse::FrontendRequest.new(self)
75
+ end
76
+ offset = @request.receive_data(data)
77
+ data = data[offset..-1]
78
+ end
79
+ rescue HTTP::Parser::Error => e
80
+ raise e if e.message != 'Could not parse data entirely'
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,71 @@
1
+ require 'rubygems'
2
+ require 'optparse'
3
+ require 'yaml'
4
+
5
+ proxyreverse_path = File.expand_path('../../lib', __FILE__)
6
+ $:.unshift(proxyreverse_path) if File.directory?(proxyreverse_path) && !$:.include?(proxyreverse_path)
7
+
8
+ require 'proxyreverse'
9
+
10
+ default_options = {
11
+ :backend_host => '127.0.0.1',
12
+ :backend_port => '80',
13
+ :host => '0.0.0.0',
14
+ :port => '80',
15
+ :rewrite_domain => false,
16
+ :verbose => false
17
+ }
18
+
19
+ options = {}
20
+
21
+ begin
22
+ cmd_args = OptionParser.new do |opts|
23
+ opts.banner = 'Usage: proxyreverse [options] [PORT] [BACKEND]'
24
+
25
+ opts.on('-r', '--rewrite-domain HOST', 'Domain to rewrite, .domain will include sub-domains') do |domain|
26
+ options[:rewrite_domain] = domain
27
+ end
28
+
29
+ opts.on('-s', '--rewrite-subdomains', 'Rewrite all subdomains') do |domain|
30
+ options[:rewrite_domain] = :subdomains
31
+ end
32
+
33
+ opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
34
+ options[:verbose] = v
35
+ end
36
+
37
+ opts.on_tail("--version", "Show version") do
38
+ puts ProxyReverse::VERSION
39
+ exit
40
+ end
41
+
42
+ opts.on_tail('-h', '--help', 'Show this message') do
43
+ puts opts
44
+ exit
45
+ end
46
+ end.parse!
47
+ rescue OptionParser::MissingArgument => e
48
+ puts e
49
+ exit
50
+ rescue OptionParser::InvalidOption => e
51
+ puts e
52
+ exit
53
+ end
54
+
55
+ options[:port] = cmd_args[0]
56
+
57
+ if cmd_args[1] =~ /^\d+$/
58
+ options[:backend_port] = cmd_args[1]
59
+ elsif cmd_args[1] =~ /^([^:]+)(?::(\d+))?/
60
+ default_options[:rewrite_domain] = $1
61
+ options[:backend_host] = $1
62
+ options[:backend_port] = $2
63
+ else
64
+ puts "Error: invalid backend syntax, expecting host/host:port/port"
65
+ exit
66
+ end
67
+
68
+ options[:version] = ProxyReverse::VERSION
69
+ options.delete_if { |k, v| v.nil? }
70
+
71
+ ProxyReverse::Client.run(default_options.merge(options))
@@ -0,0 +1,54 @@
1
+ module ProxyReverse
2
+ class FrontendRequest
3
+ attr_accessor :host
4
+
5
+ def initialize(client)
6
+ @parser = HTTP::RequestParser.new(self)
7
+ @client = client
8
+ end
9
+
10
+ def receive_data(data)
11
+ @parser << data
12
+ end
13
+
14
+ def on_message_begin
15
+ @headers = nil
16
+ @body = ''
17
+ @complete = false
18
+ end
19
+
20
+ def on_headers_complete(env)
21
+ @headers = @parser.headers
22
+ @host = @headers['Host']
23
+ @transferEncoding = 'identity'
24
+
25
+ if @client.options[:rewrite_domain]
26
+ @headers['Host'] = @client.options[:backend_host]
27
+ end
28
+
29
+ if @headers.has_key?('Transfer-Encoding')
30
+ @transferEncoding = @headers['Transfer-Encoding']
31
+ end
32
+
33
+ buf = "#{@parser.http_method} #{@parser.request_path} HTTP/#{@parser.http_version.join('.')}\r\n"
34
+ @headers.each_pair do |name, value|
35
+ buf << "#{name}: #{value}\r\n"
36
+ end
37
+ buf << "\r\n"
38
+
39
+ @client.relay_to_servers(buf)
40
+ end
41
+
42
+ def on_body(chunk)
43
+ @client.relay_to_servers(chunk)
44
+ end
45
+
46
+ def on_message_complete
47
+ @complete = true
48
+ end
49
+
50
+ def complete?
51
+ @complete
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ module ProxyReverse
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.unshift(File.expand_path('../lib', __FILE__))
3
+ require 'proxyreverse/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'proxyreverse'
7
+ s.version = ProxyReverse::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Andy Thompson']
10
+ s.email = ['me@andytson.com']
11
+ s.homepage = 'http://github.com/andytson/proxyreverse'
12
+ s.summary = 'Reverse proxy your local web-server'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
17
+
18
+ s.add_dependency('em-proxy', '~> 0.1.8')
19
+ s.add_dependency('http_parser.rb', '~> 0.5.3')
20
+ s.add_development_dependency('bundler', '>= 1.0.10')
21
+ s.add_development_dependency('rake', '>= 0.8.7')
22
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: proxyreverse
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andy Thompson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: em-proxy
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.8
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.1.8
30
+ - !ruby/object:Gem::Dependency
31
+ name: http_parser.rb
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.5.3
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.5.3
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.10
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.10
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.8.7
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.8.7
78
+ description:
79
+ email:
80
+ - me@andytson.com
81
+ executables:
82
+ - proxyreverse
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - Gemfile
87
+ - LICENSE
88
+ - Manifest
89
+ - README.md
90
+ - Rakefile
91
+ - bin/proxyreverse
92
+ - lib/proxyreverse.rb
93
+ - lib/proxyreverse/backendresponse.rb
94
+ - lib/proxyreverse/client.rb
95
+ - lib/proxyreverse/command.rb
96
+ - lib/proxyreverse/frontendrequest.rb
97
+ - lib/proxyreverse/version.rb
98
+ - proxylocal-reverse.gemspec
99
+ homepage: http://github.com/andytson/proxyreverse
100
+ licenses: []
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 1.8.23
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: Reverse proxy your local web-server
123
+ test_files: []