Of course, some users will prefer to bind HTTP application servers to Unix domain sockets for better isolation and (maybe) better performance. --- extras/proxy_pass.rb | 7 +++-- test/test_extras_proxy_pass.rb | 69 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/extras/proxy_pass.rb b/extras/proxy_pass.rb index d435ebe..00adf18 100644 --- a/extras/proxy_pass.rb +++ b/extras/proxy_pass.rb @@ -112,12 +112,15 @@ class ProxyPass # :nodoc: def initialize(dest, timeout = 5) 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" + raise ArgumentError, "destination must be an HTTP URL or unix: path" end init_path_vars(path) @pool = ConnPool.new @@ -125,7 +128,7 @@ class ProxyPass # :nodoc: end def init_path_vars(path) - path ||= '$(fullpath)' + path ||= '$fullpath' # methods from Rack::Request we want: allow = %w(fullpath host_with_port host port url path) want = path.scan(/\$(\w+)/).flatten! || [] diff --git a/test/test_extras_proxy_pass.rb b/test/test_extras_proxy_pass.rb index 8842683..5bbce73 100644 --- a/test/test_extras_proxy_pass.rb +++ b/test/test_extras_proxy_pass.rb @@ -1,6 +1,7 @@ # Copyright (C) 2015 all contributors <yahns-public@yhbt.net> # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt) require_relative 'server_helper' +require 'json' class TestExtrasProxyPass < Testcase ENV["N"].to_i > 1 and parallelize_me! @@ -34,6 +35,74 @@ class TestExtrasProxyPass < Testcase server_helper_teardown end + def test_unix_socket_no_path + tmpdir = Dir.mktmpdir + unix_path = "#{tmpdir}/proxy_pass.sock" + unix_srv = UNIXServer.new(unix_path) + err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1] + host2, port2 = @srv2.addr[3], @srv2.addr[1] + pid = mkserver(cfg) do + @srv.autoclose = @srv2.autoclose = false + ENV["YAHNS_FD"] = "#{@srv.fileno},#{@srv2.fileno}" + $LOAD_PATH.unshift "#{Dir.pwd}/extras" + require 'proxy_pass' + cfg.instance_eval do + app(:rack, ProxyPass.new("unix:#{unix_path}:/$fullpath")) do + listen "#{host}:#{port}" + end + app(:rack, ProxyPass.new("unix:#{unix_path}:/foo$fullpath")) do + listen "#{host2}:#{port2}" + end + stderr_path err.path + end + end + + pid2 = mkserver(cfg, unix_srv) do + @srv.close + @srv2.close + cfg.instance_eval do + rapp = lambda do |env| + body = env.to_json + hdr = { + 'Content-Length' => body.bytesize.to_s, + 'Content-Type' => 'application/json', + } + [ 200, hdr, [ body ] ] + end + app(:rack, rapp) { listen unix_path } + stderr_path err.path + end + end + Net::HTTP.start(host, port) do |http| + res = http.request(Net::HTTP::Get.new('/f00')) + assert_equal 200, res.code.to_i + body = JSON.parse(res.body) + assert_equal '/f00', body['PATH_INFO'] + + res = http.request(Net::HTTP::Get.new('/f00foo')) + assert_equal 200, res.code.to_i + body = JSON.parse(res.body) + assert_equal '/f00foo', body['PATH_INFO'] + end + + Net::HTTP.start(host2, port2) do |http| + res = http.request(Net::HTTP::Get.new('/Foo')) + assert_equal 200, res.code.to_i + body = JSON.parse(res.body) + assert_equal '/foo/Foo', body['PATH_INFO'] + + res = http.request(Net::HTTP::Get.new('/Foofoo')) + assert_equal 200, res.code.to_i + body = JSON.parse(res.body) + assert_equal '/foo/Foofoo', body['PATH_INFO'] + end + ensure + quit_wait(pid) + quit_wait(pid2) + unix_srv.close if unix_srv + FileUtils.rm_rf(tmpdir) if tmpdir + end + def test_proxy_pass err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1] host2, port2 = @srv2.addr[3], @srv2.addr[1] -- EW