1 # -*- encoding: binary -*-
8 include Socket::Constants
10 # prevents IO objects in here from being GC-ed
13 # internal interface, only used by Rainbows!/Zbatery
15 # The semantics for TCP_DEFER_ACCEPT changed in Linux 2.6.32+
16 # with commit d1b99ba41d6c5aa1ed2fc634323449dd656899e9
17 # This change shouldn't affect Unicorn users behind nginx (a
18 # value of 1 remains an optimization), but Rainbows! users may
19 # want to use a higher value on Linux 2.6.32+ to protect against
20 # denial-of-service attacks
21 :tcp_defer_accept => 1,
23 # FreeBSD, we need to override this to 'dataready' if we
24 # eventually get HTTPS support
25 :accept_filter => 'httpready',
27 # same default value as Mongrel
30 # since we don't do keepalive, we'll always flush-on-close and
31 # this saves packets for everyone.
36 # configure platform-specific options (only tested on Linux 2.6 so far)
39 # from /usr/include/linux/tcp.h
40 TCP_DEFER_ACCEPT = 9 unless defined?(TCP_DEFER_ACCEPT)
42 # do not send out partial frames (Linux)
43 TCP_CORK = 3 unless defined?(TCP_CORK)
45 # do not send out partial frames (FreeBSD)
46 TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
49 [ af_name, nil ].pack('a16a240')
50 end if defined?(SO_ACCEPTFILTER)
53 def set_tcp_sockopt(sock, opt)
54 # highly portable, but off by default because we don't do keepalive
55 if defined?(TCP_NODELAY)
56 val = opt[:tcp_nodelay]
57 val = DEFAULTS[:tcp_nodelay] if nil == val
58 sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
61 val = opt[:tcp_nopush]
62 val = DEFAULTS[:tcp_nopush] if nil == val
64 if defined?(TCP_CORK) # Linux
65 sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
66 elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
67 sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
70 # No good reason to ever have deferred accepts off
71 # (except maybe benchmarking)
72 if defined?(TCP_DEFER_ACCEPT)
73 # this differs from nginx, since nginx doesn't allow us to
74 # configure the the timeout...
75 seconds = opt[:tcp_defer_accept]
76 seconds = DEFAULTS[:tcp_defer_accept] if [true,nil].include?(seconds)
77 seconds = 0 unless seconds # nil/false means disable this
78 sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
79 elsif respond_to?(:accf_arg)
80 name = opt[:accept_filter]
81 name = DEFAULTS[:accept_filter] if nil == name
83 sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
85 logger.error("#{sock_name(sock)} " \
86 "failed to set accept_filter=#{name} (#{e.inspect})")
91 def set_server_sockopt(sock, opt)
92 opt = DEFAULTS.merge(opt || {})
94 TCPSocket === sock and set_tcp_sockopt(sock, opt)
96 if opt[:rcvbuf] || opt[:sndbuf]
97 log_buffer_sizes(sock, "before: ")
98 sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
99 sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
100 log_buffer_sizes(sock, " after: ")
102 sock.listen(opt[:backlog])
104 logger.error "error setting socket options: #{e.inspect}"
105 logger.error e.backtrace.join("\n")
108 def log_buffer_sizes(sock, pfx = '')
109 rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
110 sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
111 logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
114 # creates a new server, socket. address may be a HOST:PORT or
115 # an absolute path to a UNIX socket. address can even be a Socket
116 # object in which case it is immediately returned
117 def bind_listen(address = '0.0.0.0:8080', opt = {})
118 return address unless String === address
120 sock = if address[0] == ?/
121 if File.exist?(address)
122 if File.socket?(address)
124 UNIXSocket.new(address).close
125 # fall through, try to bind(2) and fail with EADDRINUSE
126 # (or succeed from a small race condition we can't sanely avoid).
127 rescue Errno::ECONNREFUSED
128 logger.info "unlinking existing socket=#{address}"
133 "socket=#{address} specified but it is not a socket!"
136 old_umask = File.umask(opt[:umask] || 0)
138 Kgio::UNIXServer.new(address)
140 File.umask(old_umask)
142 elsif /\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address
143 new_ipv6_server($1, $2.to_i, opt)
144 elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address
145 Kgio::TCPServer.new($1, $2.to_i)
147 raise ArgumentError, "Don't know how to bind: #{address}"
149 set_server_sockopt(sock, opt)
153 def new_ipv6_server(addr, port, opt)
154 opt.key?(:ipv6only) or return Kgio::TCPServer.new(addr, port)
155 defined?(IPV6_V6ONLY) or
156 abort "Socket::IPV6_V6ONLY not defined, upgrade Ruby and/or your OS"
157 sock = Socket.new(AF_INET6, SOCK_STREAM, 0)
158 sock.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
159 sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
160 sock.bind(Socket.pack_sockaddr_in(port, addr))
162 Kgio::TCPServer.for_fd(sock.fileno)
165 # returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
167 port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
168 /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
170 module_function :tcp_name
172 # Returns the configuration name of a socket as a string. sock may
173 # be a string value, in which case it is returned as-is
174 # Warning: TCP sockets may not always return the name given to it.
177 when String then sock
179 Socket.unpack_sockaddr_un(sock.getsockname)
186 Socket.unpack_sockaddr_un(sock.getsockname)
189 raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
193 module_function :sock_name
195 # casts a given Socket to be a TCPServer or UNIXServer
196 def server_cast(sock)
198 Socket.unpack_sockaddr_in(sock.getsockname)
199 Kgio::TCPServer.for_fd(sock.fileno)
201 Kgio::UNIXServer.for_fd(sock.fileno)
205 end # module SocketHelper