1 # -*- encoding: binary -*-
8 include Socket::Constants
10 # prevents IO objects in here from being GC-ed
11 # kill this when we drop 1.8 support
14 # internal interface, only used by Rainbows!/Zbatery
16 # The semantics for TCP_DEFER_ACCEPT changed in Linux 2.6.32+
17 # with commit d1b99ba41d6c5aa1ed2fc634323449dd656899e9
18 # This change shouldn't affect Unicorn users behind nginx (a
19 # value of 1 remains an optimization), but Rainbows! users may
20 # want to use a higher value on Linux 2.6.32+ to protect against
21 # denial-of-service attacks
22 :tcp_defer_accept => 1,
24 # FreeBSD, we need to override this to 'dataready' if we
25 # eventually get HTTPS support
26 :accept_filter => 'httpready',
28 # same default value as Mongrel
31 # favor latency over bandwidth savings
37 # configure platform-specific options (only tested on Linux 2.6 so far)
40 # from /usr/include/linux/tcp.h
41 TCP_DEFER_ACCEPT = 9 unless defined?(TCP_DEFER_ACCEPT)
43 # do not send out partial frames (Linux)
44 TCP_CORK = 3 unless defined?(TCP_CORK)
46 # Linux got SO_REUSEPORT in 3.9, BSDs have had it for ages
47 unless defined?(SO_REUSEPORT)
48 if RUBY_PLATFORM =~ /(?:alpha|mips|parisc|sparc)/
49 SO_REUSEPORT = 0x0200 # untested
51 SO_REUSEPORT = 15 # only tested on x86_64 and i686
55 # do not send out partial frames (FreeBSD)
56 TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
59 [ af_name, nil ].pack('a16a240')
60 end if defined?(SO_ACCEPTFILTER)
63 def prevent_autoclose(io)
64 if io.respond_to?(:autoclose=)
71 def set_tcp_sockopt(sock, opt)
72 # just in case, even LANs can break sometimes. Linux sysadmins
73 # can lower net.ipv4.tcp_keepalive_* sysctl knobs to very low values.
74 sock.setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1) if defined?(SO_KEEPALIVE)
76 if defined?(TCP_NODELAY)
77 val = opt[:tcp_nodelay]
78 val = DEFAULTS[:tcp_nodelay] if nil == val
79 sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
82 val = opt[:tcp_nopush]
84 if defined?(TCP_CORK) # Linux
85 sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
86 elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is lightly tested (FreeBSD)
87 sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
91 # No good reason to ever have deferred accepts off
92 # (except maybe benchmarking)
93 if defined?(TCP_DEFER_ACCEPT)
94 # this differs from nginx, since nginx doesn't allow us to
95 # configure the the timeout...
96 seconds = opt[:tcp_defer_accept]
97 seconds = DEFAULTS[:tcp_defer_accept] if [true,nil].include?(seconds)
98 seconds = 0 unless seconds # nil/false means disable this
99 sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
100 elsif respond_to?(:accf_arg)
101 name = opt[:accept_filter]
102 name = DEFAULTS[:accept_filter] if nil == name
104 sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
106 logger.error("#{sock_name(sock)} " \
107 "failed to set accept_filter=#{name} (#{e.inspect})")
112 def set_server_sockopt(sock, opt)
113 opt = DEFAULTS.merge(opt || {})
115 TCPSocket === sock and set_tcp_sockopt(sock, opt)
117 if opt[:rcvbuf] || opt[:sndbuf]
118 log_buffer_sizes(sock, "before: ")
119 sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
120 sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
121 log_buffer_sizes(sock, " after: ")
123 sock.listen(opt[:backlog])
125 Unicorn.log_error(logger, "#{sock_name(sock)} #{opt.inspect}", e)
128 def log_buffer_sizes(sock, pfx = '')
129 rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
130 sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
131 logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
134 # creates a new server, socket. address may be a HOST:PORT or
135 # an absolute path to a UNIX socket. address can even be a Socket
136 # object in which case it is immediately returned
137 def bind_listen(address = '0.0.0.0:8080', opt = {})
138 return address unless String === address
140 sock = if address[0] == ?/
141 if File.exist?(address)
142 if File.socket?(address)
144 UNIXSocket.new(address).close
145 # fall through, try to bind(2) and fail with EADDRINUSE
146 # (or succeed from a small race condition we can't sanely avoid).
147 rescue Errno::ECONNREFUSED
148 logger.info "unlinking existing socket=#{address}"
153 "socket=#{address} specified but it is not a socket!"
156 old_umask = File.umask(opt[:umask] || 0)
158 Kgio::UNIXServer.new(address)
160 File.umask(old_umask)
162 elsif /\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address
163 new_tcp_server($1, $2.to_i, opt.merge(:ipv6=>true))
164 elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address
165 new_tcp_server($1, $2.to_i, opt)
167 raise ArgumentError, "Don't know how to bind: #{address}"
169 set_server_sockopt(sock, opt)
173 def new_tcp_server(addr, port, opt)
174 # n.b. we set FD_CLOEXEC in the workers
175 sock = Socket.new(opt[:ipv6] ? AF_INET6 : AF_INET, SOCK_STREAM, 0)
176 if opt.key?(:ipv6only)
177 defined?(IPV6_V6ONLY) or
178 abort "Socket::IPV6_V6ONLY not defined, upgrade Ruby and/or your OS"
179 sock.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
181 sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
182 if defined?(SO_REUSEPORT) && opt[:reuseport]
183 sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
185 sock.bind(Socket.pack_sockaddr_in(port, addr))
186 prevent_autoclose(sock)
187 Kgio::TCPServer.for_fd(sock.fileno)
190 # returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
192 port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
193 /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
195 module_function :tcp_name
197 # Returns the configuration name of a socket as a string. sock may
198 # be a string value, in which case it is returned as-is
199 # Warning: TCP sockets may not always return the name given to it.
202 when String then sock
204 Socket.unpack_sockaddr_un(sock.getsockname)
211 Socket.unpack_sockaddr_un(sock.getsockname)
214 raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
218 module_function :sock_name
220 # casts a given Socket to be a TCPServer or UNIXServer
221 def server_cast(sock)
223 Socket.unpack_sockaddr_in(sock.getsockname)
224 Kgio::TCPServer.for_fd(sock.fileno)
226 Kgio::UNIXServer.for_fd(sock.fileno)
230 end # module SocketHelper