Class: SC::Rack::Proxy

Inherits:
Object
  • Object
show all
Defined in:
lib/sproutcore/rack/proxy.rb

Overview

Rack application proxies requests as needed for the given project.

Instance Method Summary collapse

Constructor Details

#initialize(project) ⇒ Proxy

Returns a new instance of Proxy.



15
16
17
18
# File 'lib/sproutcore/rack/proxy.rb', line 15

def initialize(project) 
  @project = project
  @proxies = project.buildfile.proxies
end

Instance Method Details

#call(env) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
# File 'lib/sproutcore/rack/proxy.rb', line 20

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

#handle_proxy(proxy, proxy_url, env) ⇒ Object



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
# File 'lib/sproutcore/rack/proxy.rb', line 32

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
    headers[key] = value
  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) 
  ::Net::HTTP.start(http_host, http_port) do |http|
    if no_body_method.include?(http_method)
      response = http.send(http_method, http_path, headers)
    else
    	http_body = env['rack.input'].gets || ''
      response = http.send(http_method, http_path, http_body, headers)
    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'
    
    SC.logger << "   #{key}: #{value}\n"
    response_headers[key] = value
  end
  
  return [status, ::Rack::Utils::HeaderHash.new(response_headers), [response.body]]
end