Created
October 5, 2011 08:35
-
-
Save jeroenvandijk/1263937 to your computer and use it in GitHub Desktop.
Middleman Proxy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#... | |
# assuming the proxy.rb file is in your project in middleman/proxy | |
require File.expand_path('middleman/proxy', File.dirname(__FILE__)) | |
activate :proxy | |
# proxy all path starting with tweet to twitter | |
proxy '/tweet/', :to => "twitter.com" | |
#... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# =========================================================================== | |
# Original Project: Abbot - SproutCore Build Tools | |
# Copyright: ©2009 Apple Inc. | |
# portions copyright @2006-2011 Strobe Inc. | |
# and contributors | |
# =========================================================================== | |
begin | |
require 'net/https' | |
Middleman::HTTPS_ENABLED = true | |
rescue LoadError => e | |
require 'net/http' | |
Middleman::HTTPS_ENABLED = false | |
end | |
module Middleman::Features::Proxy | |
class << self | |
def registered(app) | |
app.extend ClassMethods | |
app.use Middleman::Features::Proxy::Rack | |
end | |
alias :included :registered | |
end | |
class Collection | |
def initialize(app) | |
@app = app | |
end | |
def self.proxies | |
@@proxies ||= {} | |
end | |
def self.add(path, options={}) | |
@@proxies ||= {} | |
@@proxies[path] = options | |
end | |
end | |
module ClassMethods | |
# Proxies requests to the path | |
# | |
# proxy '/twitter', "http://twitter/web/service" | |
def proxy(path, options={}) | |
Middleman::Features::Proxy::Collection.add(path, options) | |
end | |
end | |
# Rack application proxies requests as needed for the given project. | |
class Rack | |
def initialize(app) | |
@app = app | |
end | |
def call(env) | |
url = env['PATH_INFO'] | |
@proxies = Middleman::Features::Proxy::Collection.proxies | |
@proxies.each do |proxy, value| | |
if url.match(/^#{Regexp.escape(proxy.to_s)}/) | |
puts "#{url} matches #{proxy}" | |
return handle_proxy(value, proxy.to_s, env) | |
end | |
end | |
@app.call(env) | |
# return [404, {}, "not found"] | |
end | |
def handle_proxy(proxy, proxy_url, env) | |
if proxy[:secure] && !Middleman::HTTPS_ENABLED | |
$stderr.puts "~ WARNING: HTTPS is not supported on your system, using HTTP instead.\n" | |
$stderr.puts" If you are using Ubuntu, you can run `apt-get install libopenssl-ruby`\n" | |
proxy[:secure] = false | |
end | |
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'] | |
length = env['CONTENT_LENGTH'] | |
headers['Content-Length'] = length if length | |
http_host, http_port = proxy[:to].split(':') | |
http_port = proxy[:secure] ? '443' : '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(get copy head move options trace) | |
done = false | |
tries = 0 | |
until done | |
http = ::Net::HTTP.new(http_host, http_port) | |
if proxy[:secure] | |
http.use_ssl = true | |
http.verify_mode = OpenSSL::SSL::VERIFY_NONE | |
end | |
http.start do |web| | |
if no_body_method.include?(http_method) | |
response = web.send(http_method, http_path, headers) | |
else | |
http_body = env['rack.input'] | |
http_body.rewind # May not be necessary but can't hurt | |
req = Net::HTTPGenericRequest.new(http_method.upcase, | |
true, true, http_path, headers) | |
req.body_stream = http_body if length.to_i > 0 | |
response = web.request(req) | |
end | |
end | |
status = response.code # http status code | |
protocol = proxy[:secure] ? 'https' : 'http' | |
$stderr.puts "~ PROXY: #{http_method.upcase} #{status} #{url} -> #{protocol}://#{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 | |
$stderr << " #{key}: #{value}\n" | |
response_headers[key] = value | |
end | |
if [301, 302, 303, 307].include?(status.to_i) && proxy[:redirect] != false | |
$stderr.puts '~ REDIRECTING: '+response_headers['location']+"\n" | |
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 | |
tries += 1 | |
if tries > 10 | |
raise "Too many redirects!" | |
end | |
else | |
done = true | |
end | |
end | |
# Thin doesn't like null bodies | |
response_body = response.body || '' | |
return [status, ::Rack::Utils::HeaderHash.new(response_headers), [response_body]] | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment