/
proxy.rb
116 lines (91 loc) · 3.67 KB
/
proxy.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
require "net_http_hacked"
require "rack/http_streaming_response"
module Rack
# Subclass and bring your own #rewrite_request and #rewrite_response
class Proxy
VERSION = "0.5.10"
class << self
def extract_http_request_headers(env)
headers = env.reject do |k, v|
!(/^HTTP_[A-Z_]+$/ === k) || v.nil?
end.map do |k, v|
[reconstruct_header_name(k), v]
end.inject(Utils::HeaderHash.new) do |hash, k_v|
k, v = k_v
hash[k] = v
hash
end
x_forwarded_for = (headers["X-Forwarded-For"].to_s.split(/, +/) << env["REMOTE_ADDR"]).join(", ")
headers.merge!("X-Forwarded-For" => x_forwarded_for)
end
def normalize_headers(headers)
mapped = headers.map do|k, v|
[k, if v.is_a? Array then v.first else v end]
end
Hash[mapped]
end
protected
def reconstruct_header_name(name)
name.sub(/^HTTP_/, "").gsub("_", "-")
end
end
# @option opts [String, URI::HTTP] :backend Backend host to proxy requests to
def initialize(opts={})
@streaming = opts.fetch(:streaming, true)
@ssl_verify_none = opts.fetch(:ssl_verify_none, false)
@backend = URI(opts[:backend]) if opts[:backend]
end
def call(env)
rewrite_response(perform_request(rewrite_env(env)))
end
# Return modified env
def rewrite_env(env)
env
end
# Return a rack triplet [status, headers, body]
def rewrite_response(triplet)
triplet
end
protected
def perform_request(env)
source_request = Rack::Request.new(env)
# Initialize request
if source_request.fullpath == ""
full_path = URI.parse(env['REQUEST_URI']).request_uri
else
full_path = source_request.fullpath
end
target_request = Net::HTTP.const_get(source_request.request_method.capitalize).new(full_path)
# Setup headers
target_request.initialize_http_header(self.class.extract_http_request_headers(source_request.env))
# Setup body
if target_request.request_body_permitted? && source_request.body
target_request.body_stream = source_request.body
target_request.content_length = source_request.content_length.to_i
target_request.content_type = source_request.content_type if source_request.content_type
target_request.body_stream.rewind
end
# Create a streaming response (the actual network communication is deferred, a.k.a. streamed)
target_response = HttpStreamingResponse.new(target_request, source_request.host, source_request.port)
backend = @backend || source_request
use_ssl = backend.scheme == "https"
ssl_verify_none = (env.delete('rack.ssl_verify_none') || @ssl_verify_none) == true
# Create the response
if @streaming
# streaming response (the actual network communication is deferred, a.k.a. streamed)
target_response = HttpStreamingResponse.new(target_request, backend.host, backend.port)
target_response.use_ssl = use_ssl
target_response.verify_mode = OpenSSL::SSL::VERIFY_NONE if use_ssl && ssl_verify_none
else
target_response = Net::HTTP.start(backend.host, backend.port, :use_ssl => use_ssl) do |http|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if use_ssl && ssl_verify_none
http.request(target_request)
end
end
headers = (target_response.respond_to?(:headers) && target_response.headers) || self.class.normalize_headers(target_response.to_hash)
body = target_response.body
body = [body] unless body.respond_to?(:each)
[target_response.code, headers, body]
end
end
end