-
Notifications
You must be signed in to change notification settings - Fork 17
/
proxy.rb
144 lines (119 loc) · 4.92 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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# ===========================================================================
# Project: Abbot - SproutCore Build Tools
# Copyright: ©2009 Apple Inc.
# portions copyright @2006-2009 Sprout Systems, Inc.
# and contributors
# ===========================================================================
require 'net/https'
module SC
module Rack
# Rack application proxies requests as needed for the given project.
class Proxy
def initialize(project)
@project = project
@proxies = project.buildfile.proxies
end
def call(env)
url = env['PATH_INFO']
@proxies.each do |proxy, value|
if url.match(/^#{Regexp.escape(proxy.to_s)}/)
return handle_proxy(value, proxy.to_s, env)
end
end
return [404, {}, "not found"]
end
def handle_proxy(proxy, proxy_url, env)
origin_host = env['SERVER_NAME'] # capture the origin host for cookies
http_method = env['REQUEST_METHOD'].to_s.downcase
url = env['PATH_INFO']
params = env['QUERY_STRING']
# collect headers...
headers = {}
env.each do |key, value|
next unless key =~ /^HTTP_/
key = key.gsub(/^HTTP_/,'').downcase.sub(/^\w/){|l| l.upcase}.gsub(/_(\w)/){|l| "-#{$1.upcase}"} # remove HTTP_, dasherize and titleize
if !key.eql? "Version"
headers[key] = value
end
end
# Rack documentation says CONTENT_TYPE and CONTENT_LENGTH aren't prefixed by HTTP_
headers['Content-Type'] = env['CONTENT_TYPE'] if env['CONTENT_TYPE']
headers['Content-Length'] = env['CONTENT_LENGTH'] if env['CONTENT_LENGTH']
http_host, http_port = proxy[:to].split(':')
http_port = '80' if http_port.nil?
# added 4/23/09 per Charles Jolley, corrects problem
# when making requests to virtual hosts
headers['Host'] = "#{http_host}:#{http_port}"
if proxy[:url]
url = url.sub(/^#{Regexp.escape proxy_url}/, proxy[:url])
end
http_path = [url]
http_path << params if params && params.size>0
http_path = http_path.join('?')
response = nil
no_body_method = %w(delete get copy head move options trace)
done = false
tries = 0
until done
::Net::HTTP.start(http_host, http_port) do |http|
if proxy[:secure]
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
if no_body_method.include?(http_method)
response = http.send(http_method, http_path, headers)
else
http_body = env['rack.input']
some_request = Net::HTTPGenericRequest.new http_method.upcase,
true, true, http_path, headers
some_request.body_stream = http_body
response = http.request(some_request)
end
end
status = response.code # http status code
SC.logger << "~ PROXY: #{http_method.upcase} #{status} #{url} -> http://#{http_host}:#{http_port}#{http_path}\n"
# display and construct specific response headers
response_headers = {}
ignore_headers = ['transfer-encoding', 'keep-alive', 'connection']
response.each do |key, value|
next if ignore_headers.include?(key.downcase)
# If this is a cookie, strip out the domain. This technically may
# break certain scenarios where services try to set cross-domain
# cookies, but those services should not be doing that anyway...
value.gsub!(/domain=[^\;]+\;? ?/,'') if key.downcase == 'set-cookie'
# Location headers should rewrite the hostname if it is included.
value.gsub!(/^http:\/\/#{http_host}(:[0-9]+)?\//, "http://#{http_host}/") if key.downcase == 'location'
# content-length is returning char count not bytesize
if key.downcase == 'content-length'
if response.body.respond_to?(:bytesize)
value = response.body.bytesize.to_s
elsif response.body.respond_to?(:size)
value = response.body.size.to_s
else
value = '0'
end
end
SC.logger << " #{key}: #{value}\n"
response_headers[key] = value
end
if [301, 307].include?(status.to_i)
SC.logger << '~ REDIRECTING: '+response_headers['location']
uri = URI.parse(response_headers['location']);
http_host = uri.host
http_port = uri.port
http_path = uri.path
http_path += '?'+uri.query if uri.query
headers = {}
tries += 1
if tries > 10
raise "Too many redirects!"
end
else
done = true
end
end
return [status, ::Rack::Utils::HeaderHash.new(response_headers), [response.body]]
end
end
end
end