Class: Yahns::ProxyPass
- Inherits:
-
Object
- Object
- Yahns::ProxyPass
- Defined in:
- lib/yahns/proxy_pass.rb
Overview
:nodoc:
Instance Attribute Summary collapse
-
#proxy_buffering ⇒ Object
readonly
Returns the value of attribute proxy_buffering.
-
#response_headers ⇒ Object
readonly
Returns the value of attribute response_headers.
Instance Method Summary collapse
- #call(env) ⇒ Object
- #init_path_vars(path) ⇒ Object
-
#initialize(dest, opts = {}) ⇒ ProxyPass
constructor
A new instance of ProxyPass.
Constructor Details
#initialize(dest, opts = {}) ⇒ ProxyPass
Returns a new instance of ProxyPass.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/yahns/proxy_pass.rb', line 21 def initialize(dest, opts = {}) case dest when %r{\Aunix:([^:]+)(?::(/.*))?\z} path = $2 @sockaddr = Socket.sockaddr_un($1) when %r{\Ahttp://([^/]+)(/.*)?\z} path = $2 host, port = $1.split(':') @sockaddr = Socket.sockaddr_in(port || 80, host) else raise ArgumentError, "destination must be an HTTP URL or unix: path" end @response_headers = opts[:response_headers] || {} @proxy_buffering = opts[:proxy_buffering] @proxy_buffering = true if @proxy_buffering.nil? # allow false # It's wrong to send the backend Server tag through. Let users say # { "Server => "yahns" } if they want to advertise for us, but don't # advertise by default (for security) @response_headers['Server'] ||= :ignore init_path_vars(path) end |
Instance Attribute Details
#proxy_buffering ⇒ Object (readonly)
Returns the value of attribute proxy_buffering.
19 20 21 |
# File 'lib/yahns/proxy_pass.rb', line 19 def proxy_buffering @proxy_buffering end |
#response_headers ⇒ Object (readonly)
Returns the value of attribute response_headers.
19 20 21 |
# File 'lib/yahns/proxy_pass.rb', line 19 def response_headers @response_headers end |
Instance Method Details
#call(env) ⇒ Object
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 |
# File 'lib/yahns/proxy_pass.rb', line 57 def call(env) # 3-way handshake for TCP backends while we generate the request header rr = Yahns::ReqRes.start(@sockaddr) c = env['rack.hijack'].call # Yahns::HttpClient#call req = Rack::Request.new(env) req = @path.gsub(/\$(\w+)/) { req.__send__($1) } # start the connection asynchronously and early so TCP can do a case ver = env['HTTP_VERSION'] when 'HTTP/1.1' # leave alone, response may be chunked else # no chunking for HTTP/1.0 and HTTP/0.9 ver = 'HTTP/1.0'.freeze end addr = env['REMOTE_ADDR'] xff = env['HTTP_X_FORWARDED_FOR'] xff = xff =~ /\S/ ? "#{xff}, #{addr}" : addr req = "#{env['REQUEST_METHOD']} #{req} #{ver}\r\n" \ "X-Forwarded-Proto: #{env['rack.url_scheme']}\r\n" \ "X-Forwarded-For: #{xff}\r\n".dup # pass most HTTP_* headers through as-is chunked = false env.each do |key, val| %r{\AHTTP_(\w+)\z} =~ key or next key = $1 # trailers are folded into the header, so do not send the Trailer: # header in the request next if /\A(?:VERSION|CONNECTION|KEEP_ALIVE|X_FORWARDED_FOR|TRAILER)/ =~ key 'TRANSFER_ENCODING'.freeze == key && val =~ /\bchunked\b/i and chunked = true key.tr!('_'.freeze, '-'.freeze) req << "#{key}: #{val}\r\n" end # special cases which Rack does not prefix: ctype = env["CONTENT_TYPE"] and req << "Content-Type: #{ctype}\r\n" clen = env["CONTENT_LENGTH"] and req << "Content-Length: #{clen}\r\n" input = chunked || (clen && clen.to_i > 0) ? env['rack.input'] : nil # finally, prepare to emit the headers rr.req_start(c, req << "\r\n".freeze, input, chunked, self) # this probably breaks fewer middlewares than returning whatever else... [ 500, [], [] ] rescue => e Yahns::Log.exception(env['rack.logger'], 'proxy_pass', e) [ 502, { 'Content-Length' => '0', 'Content-Type' => 'text/plain' }, [] ] end |
#init_path_vars(path) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/yahns/proxy_pass.rb', line 44 def init_path_vars(path) path ||= '$fullpath' # methods from Rack::Request we want: allow = %w(fullpath host_with_port host port url path) want = path.scan(/\$(\w+)/).flatten! || [] diff = want - allow diff.empty? or raise ArgumentError, "vars not allowed: #{diff.uniq.join(' ')}" # kill leading slash just in case... @path = path.gsub(%r{\A/(\$(?:fullpath|path))}, '\1') end |