1 # -*- encoding: binary -*-
7 include Socket::Constants
10 # internal interface, only used by Rainbows!/Zbatery
12 # The semantics for TCP_DEFER_ACCEPT changed in Linux 2.6.32+
13 # with commit d1b99ba41d6c5aa1ed2fc634323449dd656899e9
14 # This change shouldn't affect Unicorn users behind nginx (a
15 # value of 1 remains an optimization), but Rainbows! users may
16 # want to use a higher value on Linux 2.6.32+ to protect against
17 # denial-of-service attacks
18 :tcp_defer_accept => 1,
20 # FreeBSD, we need to override this to 'dataready' when we
21 # eventually get HTTPS support
22 :accept_filter => 'httpready',
24 # same default value as Mongrel
27 # since we don't do keepalive, we'll always flush-on-close and
28 # this saves packets for everyone.
33 # configure platform-specific options (only tested on Linux 2.6 so far)
36 # from /usr/include/linux/tcp.h
37 TCP_DEFER_ACCEPT = 9 unless defined?(TCP_DEFER_ACCEPT)
39 # do not send out partial frames (Linux)
40 TCP_CORK = 3 unless defined?(TCP_CORK)
42 # do not send out partial frames (FreeBSD)
43 TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
46 [ af_name, nil ].pack('a16a240')
47 end if defined?(SO_ACCEPTFILTER)
50 def set_tcp_sockopt(sock, opt)
51 # highly portable, but off by default because we don't do keepalive
52 if defined?(TCP_NODELAY) && ! (val = opt[:tcp_nodelay]).nil?
53 sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
56 unless (val = opt[:tcp_nopush]).nil?
58 if defined?(TCP_CORK) # Linux
59 sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
60 elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
61 sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
65 # No good reason to ever have deferred accepts off
66 # (except maybe benchmarking)
67 if defined?(TCP_DEFER_ACCEPT)
68 # this differs from nginx, since nginx doesn't allow us to
69 # configure the the timeout...
70 seconds = opt[:tcp_defer_accept]
71 seconds = DEFAULTS[:tcp_defer_accept] if seconds == true
72 seconds = 0 unless seconds # nil/false means disable this
73 sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
74 elsif respond_to?(:accf_arg)
75 if name = opt[:accept_filter]
77 sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
79 logger.error("#{sock_name(sock)} " \
80 "failed to set accept_filter=#{name} (#{e.inspect})")
86 def set_server_sockopt(sock, opt)
87 opt = DEFAULTS.merge(opt || {})
89 TCPSocket === sock and set_tcp_sockopt(sock, opt)
91 if opt[:rcvbuf] || opt[:sndbuf]
92 log_buffer_sizes(sock, "before: ")
93 sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
94 sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
95 log_buffer_sizes(sock, " after: ")
97 sock.listen(opt[:backlog])
99 logger.error "error setting socket options: #{e.inspect}"
100 logger.error e.backtrace.join("\n")
103 def log_buffer_sizes(sock, pfx = '')
104 rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
105 sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
106 logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
109 # creates a new server, socket. address may be a HOST:PORT or
110 # an absolute path to a UNIX socket. address can even be a Socket
111 # object in which case it is immediately returned
112 def bind_listen(address = '0.0.0.0:8080', opt = {})
113 return address unless String === address
115 sock = if address[0] == ?/
116 if File.exist?(address)
117 if File.socket?(address)
119 UNIXSocket.new(address).close
120 # fall through, try to bind(2) and fail with EADDRINUSE
121 # (or succeed from a small race condition we can't sanely avoid).
122 rescue Errno::ECONNREFUSED
123 logger.info "unlinking existing socket=#{address}"
128 "socket=#{address} specified but it is not a socket!"
131 old_umask = File.umask(opt[:umask] || 0)
133 Kgio::UNIXServer.new(address)
135 File.umask(old_umask)
137 elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/
138 Kgio::TCPServer.new($1, $2.to_i)
140 raise ArgumentError, "Don't know how to bind: #{address}"
142 set_server_sockopt(sock, opt)
146 # Returns the configuration name of a socket as a string. sock may
147 # be a string value, in which case it is returned as-is
148 # Warning: TCP sockets may not always return the name given to it.
151 when String then sock
153 Socket.unpack_sockaddr_un(sock.getsockname)
155 Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
158 Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
160 Socket.unpack_sockaddr_un(sock.getsockname)
163 raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
167 module_function :sock_name
169 # casts a given Socket to be a TCPServer or UNIXServer
170 def server_cast(sock)
172 Socket.unpack_sockaddr_in(sock.getsockname)
173 Kgio::TCPServer.for_fd(sock.fileno)
175 Kgio::UNIXServer.for_fd(sock.fileno)
179 end # module SocketHelper