From 87fd86ef22b6b80fa75dd8e50f53a4e62e8339f7 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 2 Feb 2011 15:22:02 -0800 Subject: allow binding on IPv6 sockets with listen "[#{addr}]:#{port}" This is much like how nginx does it, except we always require a port when explicitly binding to IPv6 using the "listen" directive. This also adds support to listen with an address-only, which can be useful to Rainbows! users. --- lib/unicorn/configurator.rb | 14 ++++++++++---- lib/unicorn/socket_helper.rb | 14 +++++++++++--- test/unit/test_configurator.rb | 8 ++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb index 9aa84f1..8913928 100644 --- a/lib/unicorn/configurator.rb +++ b/lib/unicorn/configurator.rb @@ -473,10 +473,10 @@ class Unicorn::Configurator File.expand_path(address) when %r{\A(?:\*:)?(\d+)\z} "0.0.0.0:#$1" - when %r{\A(.*):(\d+)\z} - # canonicalize the name - packed = Socket.pack_sockaddr_in($2.to_i, $1) - Socket.unpack_sockaddr_in(packed).reverse!.join(':') + when %r{\A\[([a-fA-F0-9:]+)\]\z}, %r/\A((?:\d+\.){3}\d+)\z/ + canonicalize_tcp($1, 80) + when %r{\A\[([a-fA-F0-9:]+)\]:(\d+)\z}, %r{\A(.*):(\d+)\z} + canonicalize_tcp($1, $2.to_i) else address end @@ -489,6 +489,12 @@ private set[var] = n end + def canonicalize_tcp(addr, port) + packed = Socket.pack_sockaddr_in(port, addr) + port, addr = Socket.unpack_sockaddr_in(packed) + /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}" + end + def set_path(var, path) #:nodoc: case path when NilClass, String diff --git a/lib/unicorn/socket_helper.rb b/lib/unicorn/socket_helper.rb index 0cab3cb..e5b0735 100644 --- a/lib/unicorn/socket_helper.rb +++ b/lib/unicorn/socket_helper.rb @@ -136,7 +136,9 @@ module Unicorn ensure File.umask(old_umask) end - elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/ + elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address || + /\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address + p [ $1, $2 ] Kgio::TCPServer.new($1, $2.to_i) else raise ArgumentError, "Don't know how to bind: #{address}" @@ -145,6 +147,12 @@ module Unicorn sock end + # returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6 + def tcp_name(sock) + port, addr = Socket.unpack_sockaddr_in(sock.getsockname) + /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}" + end + # Returns the configuration name of a socket as a string. sock may # be a string value, in which case it is returned as-is # Warning: TCP sockets may not always return the name given to it. @@ -154,10 +162,10 @@ module Unicorn when UNIXServer Socket.unpack_sockaddr_un(sock.getsockname) when TCPServer - Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':') + tcp_name(sock) when Socket begin - Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':') + tcp_name(sock) rescue ArgumentError Socket.unpack_sockaddr_un(sock.getsockname) end diff --git a/test/unit/test_configurator.rb b/test/unit/test_configurator.rb index ac1efa8..c19c427 100644 --- a/test/unit/test_configurator.rb +++ b/test/unit/test_configurator.rb @@ -33,6 +33,14 @@ class TestConfigurator < Test::Unit::TestCase assert_equal "0.0.0.0:2007", meth.call('2007') assert_equal "0.0.0.0:2007", meth.call(2007) + %w([::1]:2007 [::]:2007).each do |addr| + assert_equal addr, meth.call(addr.dup) + end + + # for Rainbows! users only + assert_equal "[::]:80", meth.call("[::]") + assert_equal "127.6.6.6:80", meth.call("127.6.6.6") + # the next two aren't portable, consider them unsupported for now # assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('1:2007') # assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('2:2007') -- cgit v1.2.3-24-ge0c7