|
1 | | -require 'net/http' |
2 | | -require 'net/https' |
3 | | -require "rack-proxy" |
4 | | -require "rack/reverse_proxy_matcher" |
5 | | -require "rack/exception" |
| 1 | +require "rack_reverse_proxy" |
6 | 2 |
|
| 3 | +# Re-opening Rack module only to define ReverseProxy constant |
7 | 4 | module Rack |
8 | | - class ReverseProxy |
9 | | - include NewRelic::Agent::Instrumentation::ControllerInstrumentation if defined? NewRelic |
10 | | - |
11 | | - def initialize(app = nil, &b) |
12 | | - @app = app || lambda {|env| [404, [], []] } |
13 | | - @matchers = [] |
14 | | - @global_options = {:preserve_host => true, :x_forwarded_host => true, :matching => :all, :replace_response_host => false} |
15 | | - instance_eval(&b) if block_given? |
16 | | - end |
17 | | - |
18 | | - def call(env) |
19 | | - rackreq = Rack::Request.new(env) |
20 | | - matcher = get_matcher(rackreq.fullpath, Proxy.extract_http_request_headers(rackreq.env), rackreq) |
21 | | - return @app.call(env) if matcher.nil? |
22 | | - |
23 | | - if @global_options[:newrelic_instrumentation] |
24 | | - action_name = "#{rackreq.path.gsub(/\/\d+/,'/:id').gsub(/^\//,'')}/#{rackreq.request_method}" # Rack::ReverseProxy/foo/bar#GET |
25 | | - perform_action_with_newrelic_trace(:name => action_name, :request => rackreq) do |
26 | | - proxy(env, rackreq, matcher) |
27 | | - end |
28 | | - else |
29 | | - proxy(env, rackreq, matcher) |
30 | | - end |
31 | | - end |
32 | | - |
33 | | - private |
34 | | - |
35 | | - def proxy(env, source_request, matcher) |
36 | | - uri = matcher.get_uri(source_request.fullpath,env) |
37 | | - if uri.nil? |
38 | | - return @app.call(env) |
39 | | - end |
40 | | - options = @global_options.dup.merge(matcher.options) |
41 | | - |
42 | | - # Initialize request |
43 | | - target_request = Net::HTTP.const_get(source_request.request_method.capitalize).new(uri.request_uri) |
44 | | - |
45 | | - # Setup headers |
46 | | - target_request_headers = Proxy.extract_http_request_headers(source_request.env) |
47 | | - |
48 | | - if options[:preserve_host] |
49 | | - if uri.port == uri.default_port |
50 | | - target_request_headers['HOST'] = uri.host |
51 | | - else |
52 | | - target_request_headers['HOST'] = "#{uri.host}:#{uri.port}" |
53 | | - end |
54 | | - end |
55 | | - |
56 | | - if options[:x_forwarded_host] |
57 | | - target_request_headers['X-Forwarded-Host'] = source_request.host |
58 | | - target_request_headers['X-Forwarded-Port'] = "#{source_request.port}" |
59 | | - end |
60 | | - |
61 | | - target_request.initialize_http_header(target_request_headers) |
62 | | - |
63 | | - # Basic auth |
64 | | - target_request.basic_auth options[:username], options[:password] if options[:username] and options[:password] |
65 | | - |
66 | | - # Setup body |
67 | | - if target_request.request_body_permitted? && source_request.body |
68 | | - source_request.body.rewind |
69 | | - target_request.body_stream = source_request.body |
70 | | - end |
71 | | - |
72 | | - target_request.content_length = source_request.content_length || 0 |
73 | | - target_request.content_type = source_request.content_type if source_request.content_type |
74 | | - |
75 | | - # Create a streaming response (the actual network communication is deferred, a.k.a. streamed) |
76 | | - target_response = HttpStreamingResponse.new(target_request, uri.host, uri.port) |
77 | | - |
78 | | - # pass the timeout configuration through |
79 | | - target_response.read_timeout = options[:timeout] if options[:timeout].to_i > 0 |
80 | | - |
81 | | - target_response.use_ssl = "https" == uri.scheme |
82 | | - |
83 | | - # Let rack set the transfer-encoding header |
84 | | - response_headers = Rack::Utils::HeaderHash.new Proxy.normalize_headers(format_headers(target_response.headers)) |
85 | | - response_headers.delete('Transfer-Encoding') |
86 | | - response_headers.delete('Status') |
87 | | - |
88 | | - # Replace the location header with the proxy domain |
89 | | - if response_headers['Location'] && options[:replace_response_host] |
90 | | - response_location = URI(response_headers['location']) |
91 | | - response_location.host = source_request.host |
92 | | - response_location.port = source_request.port |
93 | | - response_headers['Location'] = response_location.to_s |
94 | | - end |
95 | | - |
96 | | - [target_response.status, response_headers, target_response.body] |
97 | | - end |
98 | | - |
99 | | - def get_matcher(path, headers, rackreq) |
100 | | - matches = @matchers.select do |matcher| |
101 | | - matcher.match?(path, headers, rackreq) |
102 | | - end |
103 | | - |
104 | | - if matches.length < 1 |
105 | | - nil |
106 | | - elsif matches.length > 1 && @global_options[:matching] != :first |
107 | | - raise AmbiguousProxyMatch.new(path, matches) |
108 | | - else |
109 | | - matches.first |
110 | | - end |
111 | | - end |
112 | | - |
113 | | - def reverse_proxy_options(options) |
114 | | - @global_options=options |
115 | | - end |
116 | | - |
117 | | - def reverse_proxy(matcher, url=nil, opts={}) |
118 | | - raise GenericProxyURI.new(url) if matcher.is_a?(String) && url.is_a?(String) && URI(url).class == URI::Generic |
119 | | - @matchers << ReverseProxyMatcher.new(matcher,url,opts) |
120 | | - end |
121 | | - |
122 | | - def format_headers(headers) |
123 | | - headers.reduce({}) do |acc, (key, val)| |
124 | | - formated_key = key.split('-').map(&:capitalize).join('-') |
125 | | - acc[formated_key] = Array(val) |
126 | | - acc |
127 | | - end |
128 | | - end |
129 | | - end |
| 5 | + ReverseProxy = RackReverseProxy::Middleware |
130 | 6 | end |
0 commit comments