|
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