1 # -*- encoding: binary -*-
7 # Instead of using a generic Kgio::Socket for everything,
8 # tag TCP sockets so we can use TCP_INFO under Linux without
9 # incurring extra syscalls for Unix domain sockets.
10 # TODO: remove these when we remove kgio
11 TCPClient = Class.new(Kgio::Socket) # :nodoc:
12 class TCPSrv < Kgio::TCPServer # :nodoc:
13 def kgio_tryaccept # :nodoc:
18 if IO.instance_method(:write).arity == 1 # Ruby <= 2.4
19 require 'unicorn/write_splat'
20 UNIXClient = Class.new(Kgio::Socket) # :nodoc:
21 class UNIXSrv < Kgio::UNIXServer # :nodoc:
22 include Unicorn::WriteSplat
23 def kgio_tryaccept # :nodoc:
27 TCPClient.__send__(:include, Unicorn::WriteSplat)
29 UNIXSrv = Kgio::UNIXServer
36 # The semantics for TCP_DEFER_ACCEPT changed in Linux 2.6.32+
37 # with commit d1b99ba41d6c5aa1ed2fc634323449dd656899e9
38 # This change shouldn't affect unicorn users behind nginx (a
39 # value of 1 remains an optimization).
40 :tcp_defer_accept => 1,
42 # FreeBSD, we need to override this to 'dataready' if we
43 # eventually support non-HTTP/1.x
44 :accept_filter => 'httpready',
46 # same default value as Mongrel
49 # favor latency over bandwidth savings
54 # configure platform-specific options (only tested on Linux 2.6 so far)
56 [ af_name, nil ].pack('a16a240')
57 end if RUBY_PLATFORM =~ /freebsd/ && Socket.const_defined?(:SO_ACCEPTFILTER)
59 def set_tcp_sockopt(sock, opt)
60 # just in case, even LANs can break sometimes. Linux sysadmins
61 # can lower net.ipv4.tcp_keepalive_* sysctl knobs to very low values.
62 Socket.const_defined?(:SO_KEEPALIVE) and
63 sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
65 if Socket.const_defined?(:TCP_NODELAY)
66 val = opt[:tcp_nodelay]
67 val = DEFAULTS[:tcp_nodelay] if val.nil?
68 sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, val ? 1 : 0)
71 val = opt[:tcp_nopush]
73 if Socket.const_defined?(:TCP_CORK) # Linux
74 sock.setsockopt(:IPPROTO_TCP, :TCP_CORK, val)
75 elsif Socket.const_defined?(:TCP_NOPUSH) # FreeBSD
76 sock.setsockopt(:IPPROTO_TCP, :TCP_NOPUSH, val)
80 # No good reason to ever have deferred accepts off in single-threaded
81 # servers (except maybe benchmarking)
82 if Socket.const_defined?(:TCP_DEFER_ACCEPT)
83 # this differs from nginx, since nginx doesn't allow us to
84 # configure the the timeout...
85 seconds = opt[:tcp_defer_accept]
86 seconds = DEFAULTS[:tcp_defer_accept] if [true,nil].include?(seconds)
87 seconds = 0 unless seconds # nil/false means disable this
88 sock.setsockopt(:IPPROTO_TCP, :TCP_DEFER_ACCEPT, seconds)
89 elsif respond_to?(:accf_arg)
90 name = opt[:accept_filter]
91 name = DEFAULTS[:accept_filter] if name.nil?
92 sock.listen(opt[:backlog])
93 got = (sock.getsockopt(:SOL_SOCKET, :SO_ACCEPTFILTER) rescue nil).to_s
96 sock.setsockopt(:SOL_SOCKET, :SO_ACCEPTFILTER, arg)
98 logger.error("#{sock_name(sock)} " \
99 "failed to set accept_filter=#{name} (#{e.inspect})")
100 logger.error("perhaps accf_http(9) needs to be loaded".freeze)
105 def set_server_sockopt(sock, opt)
106 opt = DEFAULTS.merge(opt || {})
108 TCPSocket === sock and set_tcp_sockopt(sock, opt)
110 rcvbuf, sndbuf = opt.values_at(:rcvbuf, :sndbuf)
112 log_buffer_sizes(sock, "before: ")
113 sock.setsockopt(:SOL_SOCKET, :SO_RCVBUF, rcvbuf) if rcvbuf
114 sock.setsockopt(:SOL_SOCKET, :SO_SNDBUF, sndbuf) if sndbuf
115 log_buffer_sizes(sock, " after: ")
117 sock.listen(opt[:backlog])
119 Unicorn.log_error(logger, "#{sock_name(sock)} #{opt.inspect}", e)
122 def log_buffer_sizes(sock, pfx = '')
123 rcvbuf = sock.getsockopt(:SOL_SOCKET, :SO_RCVBUF).int
124 sndbuf = sock.getsockopt(:SOL_SOCKET, :SO_SNDBUF).int
125 logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
128 # creates a new server, socket. address may be a HOST:PORT or
129 # an absolute path to a UNIX socket. address can even be a Socket
130 # object in which case it is immediately returned
131 def bind_listen(address = '0.0.0.0:8080', opt = {})
132 return address unless String === address
134 sock = if address.start_with?('/')
135 if File.exist?(address)
136 if File.socket?(address)
138 UNIXSocket.new(address).close
139 # fall through, try to bind(2) and fail with EADDRINUSE
140 # (or succeed from a small race condition we can't sanely avoid).
141 rescue Errno::ECONNREFUSED
142 logger.info "unlinking existing socket=#{address}"
147 "socket=#{address} specified but it is not a socket!"
150 old_umask = File.umask(opt[:umask] || 0)
154 File.umask(old_umask)
156 elsif /\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address
157 new_tcp_server($1, $2.to_i, opt.merge(:ipv6=>true))
158 elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address
159 new_tcp_server($1, $2.to_i, opt)
161 raise ArgumentError, "Don't know how to bind: #{address}"
163 set_server_sockopt(sock, opt)
167 def new_tcp_server(addr, port, opt)
168 # n.b. we set FD_CLOEXEC in the workers
169 sock = Socket.new(opt[:ipv6] ? :AF_INET6 : :AF_INET, :SOCK_STREAM)
170 if opt.key?(:ipv6only)
171 Socket.const_defined?(:IPV6_V6ONLY) or
172 abort "Socket::IPV6_V6ONLY not defined, upgrade Ruby and/or your OS"
173 sock.setsockopt(:IPPROTO_IPV6, :IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
175 sock.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1)
176 if Socket.const_defined?(:SO_REUSEPORT) && opt[:reuseport]
177 sock.setsockopt(:SOL_SOCKET, :SO_REUSEPORT, 1)
179 sock.bind(Socket.pack_sockaddr_in(port, addr))
180 sock.autoclose = false
181 TCPSrv.for_fd(sock.fileno)
184 # returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
186 port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
187 addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
189 module_function :tcp_name
191 # Returns the configuration name of a socket as a string. sock may
192 # be a string value, in which case it is returned as-is
193 # Warning: TCP sockets may not always return the name given to it.
196 when String then sock
198 Socket.unpack_sockaddr_un(sock.getsockname)
205 Socket.unpack_sockaddr_un(sock.getsockname)
208 raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
212 module_function :sock_name
214 # casts a given Socket to be a TCPServer or UNIXServer
215 def server_cast(sock)
217 Socket.unpack_sockaddr_in(sock.getsockname)
218 TCPSrv.for_fd(sock.fileno)
220 UNIXSrv.for_fd(sock.fileno)
224 end # module SocketHelper