Index: [Article Count Order] [Thread]

Date: Sun, 21 Mar 2004 05:44:17 +0900
From: Shinya Kawaji <kawaji@hoppeta.com>
Subject: [webrickja:115] Re: ServerAlias
To: webrickja@notwork.org
Message-Id: <20040321054417wv3a3c@hoppeta.com>
In-Reply-To: <20040319.173600.521621012.gotoyuzo@sawara.does.notwork.org>
References: <20040319.173600.521621012.gotoyuzo@sawara.does.notwork.org>
X-Mail-Count: 00115

かわじ、です。


> 10日ほど前に1.9に追加したのですが、:ServerAliasがnilの時に一
> 致させるのはよくなかったようなので、次のような修正を考えてみ
> ました。

これはもともとの私のパッチが間違っていたものでした。
ご迷惑をおかけしました。


> で、ついでによく考えてみると(エイリアスとは関係ないのですが)、
> 
>   * :BindAddressの指定ありで:Portはnil
>   * :BindAddressはnilで:Portの指定あり
> 
> というホストが同時に存在する場合に、どちらに一致するか曖昧な
> なんですね。現状ではvirtual_hostで先に追加したほうに一致する
> のですが、どちらかを優先するべきなんでしょうか。

喩えに出していいのか分かりませんが、 Apache の場合ですと、
先にIPアドレスの一致を調べてから、ポートの一致を調べるようですので、
以下のような感じになるかと思います。

# 優先順位の付け方が、もっと良い方法があると思いますが、
# とりあえず思いついた方法にしておきます。

もしもこのようにするのでしたら、warn は必要ないと思います。
でも、まあ、 virtual_host の順位でも良いような気もしますが。

Index: lib/webrick/httpserver.rb
===================================================================
RCS file: /src/ruby/lib/webrick/httpserver.rb,v
retrieving revision 1.7
diff -u -r1.7 httpserver.rb
--- lib/webrick/httpserver.rb   7 Mar 2004 16:06:42 -0000       1.7
+++ lib/webrick/httpserver.rb   20 Mar 2004 20:25:15 -0000
@@ -135,12 +135,32 @@
     end

     def lookup_server(req)
-      @virtual_hosts.find{|s|
+      servers = @virtual_hosts.select{|s|
         (s[:Port].nil?        || req.port == s[:Port])           &&
         (s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) &&
         ((s[:ServerName].nil?  || req.host == s[:ServerName]) ||
-         (s[:ServerAlias].nil? || s[:ServerAlias].find{|h| h === req.host}))
+         (!s[:ServerAlias].nil? && s[:ServerAlias].find{|h| h === req.host}))
       }
+      if (len = servers.length) > 1
+        args = [len, req.host, req.addr[3], req.port]
+        msg = "ambiguous virtual hosts (matches %d hosts for %s@%s:%d)" % args
+        @logger.warn(msg)
+        servers.sort_by{|s|
+          num = 0
+          if !s[:BindAddress].nil? and s[:BindAddress] == req.addr[3]
+            num += 4
+          end
+          if !s[:Port].nil? and s[:Port] == req.port
+            num += 2
+          end
+          if !s[:ServerName].nil? and s[:ServerName] == req.host
+            num += 1
+          end
+          num
+        }.last
+      else
+        return servers.first
+      end
     end

     def access_log(config, req, res)


> # 簡単にテストを書いてみたので付けておきます。

デバック用に、Failure になったときにどのサーバーの一致を試したときのものか、
分かるようにしてみました(ちょっと適当にやった感じですが)。

あと、shuffle を復活させて、念のため 10回 shuffle するようにしました。
(1回だけの shuffle ですと、たまたま通ってしまうこともあるようでしたので)

両方とも、不要な改造かもしれません。


require "test/unit"
require "webrick"

module WEBrick

class TestHTTPServer < Test::Unit::TestCase
  TEST_TIMES = 10

  def obj_id(obj)
    if obj.nil?
      nil
    else
      [
        obj.object_id,
        obj.config[:BindAddress].inspect,
        obj.config[:Port].inspect,
        obj.config[:ServerName].inspect,
        obj.config[:ServerAlias].inspect,
      ].join(',')
    end
  end

  def assert_eql?(v1, v2)
    assert_equal(obj_id(v1), obj_id(v2))
  end

  NullWriter = Object.new
  def NullWriter.write(msg) return msg.size end
  def NullWriter.<<(msg) return self end

  def httpd(addr, port, host, ali)
    hash = {:DoNotListen => true}
    hash[:BindAddress] = addr
    hash[:Port]        = port
    hash[:ServerName]  = host
    hash[:ServerAlias] = ali
    WEBrick::HTTPServer.new(hash)
  end

  def shuffle(ary)
    ary.replace(ary.sort_by{ rand })
  end

  class Req
    attr_reader :port, :host
    def initialize(addr, port, host)
      @addr, @port, @host = addr, port, host
    end
    def addr
      [0,0,0,@addr]
    end
  end

  def test_lookup_server
    addr1  = "192.168.100.1"
    addr2  = "192.168.100.2"
    addrz  = "192.168.100.254"
    local  = "127.0.0.1"
    port1  = 80
    port2  = 8080
    port3  = 10080
    portz  = 32767
    name1  = "www.example.com"
    name2  = "www2.example.com"
    name3  = "www3.example.com"
    namea  = "www.example.co.jp"
    nameb  = "www.example.jp"
    namec  = "www2.example.co.jp"
    named  = "www2.example.jp"
    namez  = "foobar.example.com"
    alias1 = [namea, nameb]
    alias2 = [namec, named]

    stderr = $stderr
    $stderr = NullWriter
    host1 = httpd(nil, port1, name1, nil)
    hosts = [
      host2  = httpd(addr1, port1, name1, nil),
      host3  = httpd(addr1, port1, name2, alias1),
      host4  = httpd(addr1, port2, name1, nil),
      host5  = httpd(addr1, port2, name2, alias1),
      host6  = httpd(addr1, port2, name3, alias2),
      host7  = httpd(addr2, nil,   name1, nil),
      host8  = httpd(addr2, nil,   name2, alias1),
      host9  = httpd(addr2, nil,   name3, alias2),
      host10 = httpd(local, nil,   nil,   nil),
      host11 = httpd(nil,   port3, nil,   nil),
    ]
    $stderr = stderr

    TEST_TIMES.times{
#      p hosts.collect{|h| h.object_id }
      shuffle(hosts)
#      p hosts.collect{|h| h.object_id }
      $stderr = NullWriter
      hosts.each{|h| host1.virtual_host(h) }
      $stderr = stderr

      # connect to addr1
      assert_eql?(host2,   host1.lookup_server(Req.new(addr1, port1, name1)))
      assert_eql?(host3,   host1.lookup_server(Req.new(addr1, port1, name2)))
      assert_eql?(host3,   host1.lookup_server(Req.new(addr1, port1, namea)))
      assert_eql?(host3,   host1.lookup_server(Req.new(addr1, port1, nameb)))
      assert_eql?(nil,     host1.lookup_server(Req.new(addr1, port1, namez)))
      assert_eql?(host4,   host1.lookup_server(Req.new(addr1, port2, name1)))
      assert_eql?(host5,   host1.lookup_server(Req.new(addr1, port2, name2)))
      assert_eql?(host5,   host1.lookup_server(Req.new(addr1, port2, namea)))
      assert_eql?(host5,   host1.lookup_server(Req.new(addr1, port2, nameb)))
      assert_eql?(nil,     host1.lookup_server(Req.new(addr1, port2, namez)))
      assert_eql?(host11,  host1.lookup_server(Req.new(addr1, port3, name1)))
      assert_eql?(host11,  host1.lookup_server(Req.new(addr1, port3, name2)))
      assert_eql?(host11,  host1.lookup_server(Req.new(addr1, port3, namea)))
      assert_eql?(host11,  host1.lookup_server(Req.new(addr1, port3, nameb)))
      assert_eql?(host11,  host1.lookup_server(Req.new(addr1, port3, namez)))
      assert_eql?(nil,     host1.lookup_server(Req.new(addr1, portz, name1)))
      assert_eql?(nil,     host1.lookup_server(Req.new(addr1, portz, name2)))
      assert_eql?(nil,     host1.lookup_server(Req.new(addr1, portz, namea)))
      assert_eql?(nil,     host1.lookup_server(Req.new(addr1, portz, nameb)))
      assert_eql?(nil,     host1.lookup_server(Req.new(addr1, portz, namez)))

      # connect to addr2
      assert_eql?(host7,  host1.lookup_server(Req.new(addr2, port1, name1)))
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, port1, name2)))
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, port1, namea)))
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, port1, nameb)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addr2, port1, namez)))
      assert_eql?(host7,  host1.lookup_server(Req.new(addr2, port2, name1)))
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, port2, name2)))
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, port2, namea)))
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, port2, nameb)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addr2, port2, namez)))
      assert_eql?(host7,  host1.lookup_server(Req.new(addr2, port3, name1))) #!
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, port3, name2))) #!
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, port3, namea))) #!
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, port3, nameb))) #!
      assert_eql?(host11, host1.lookup_server(Req.new(addr2, port3, namez)))
      assert_eql?(host7,  host1.lookup_server(Req.new(addr2, portz, name1)))
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, portz, name2)))
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, portz, namea)))
      assert_eql?(host8,  host1.lookup_server(Req.new(addr2, portz, nameb)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addr2, portz, namez)))

      # connect to addrz
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port1, name1)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port1, name2)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port1, namea)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port1, nameb)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port1, namez)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port2, name1)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port2, name2)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port2, namea)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port2, nameb)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, port2, namez)))
      assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name1)))
      assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name2)))
      assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namea)))
      assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, nameb)))
      assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namez)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, portz, name1)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, portz, name2)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, portz, namea)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, portz, nameb)))
      assert_eql?(nil,    host1.lookup_server(Req.new(addrz, portz, namez)))

      # connect to localhost
      assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name1)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name2)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namea)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port1, nameb)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namez)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name1)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name2)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namea)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port2, nameb)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namez)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name1))) #!
      assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name2))) #!
      assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namea))) #!
      assert_eql?(host10, host1.lookup_server(Req.new(local, port3, nameb))) #!
      assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namez))) #!
      assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name1)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name2)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namea)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, portz, nameb)))
      assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namez)))
    }
  end
end

end