Module: Unicorn::SocketHelper

Includes:
Socket::Constants
Included in:
HttpServer
Defined in:
lib/unicorn/socket_helper.rb

Constant Summary collapse

DEFAULTS =

:stopdoc: internal interface, only used by Rainbows!/Zbatery

{
  # The semantics for TCP_DEFER_ACCEPT changed in Linux 2.6.32+
  # with commit d1b99ba41d6c5aa1ed2fc634323449dd656899e9
  # This change shouldn't affect Unicorn users behind nginx (a
  # value of 1 remains an optimization), but Rainbows! users may
  # want to use a higher value on Linux 2.6.32+ to protect against
  # denial-of-service attacks
  :tcp_defer_accept => 1,

  # FreeBSD, we need to override this to 'dataready' when we
  # eventually get HTTPS support
  :accept_filter => 'httpready',
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.sock_name(sock) ⇒ Object

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.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/unicorn/socket_helper.rb', line 145

def sock_name(sock)
  case sock
  when String then sock
  when UNIXServer
    Socket.unpack_sockaddr_un(sock.getsockname)
  when TCPServer
    Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
  when Socket
    begin
      Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
    rescue ArgumentError
      Socket.unpack_sockaddr_un(sock.getsockname)
    end
  else
    raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
  end
end

Instance Method Details

#bind_listen(address = '0.0.0.0:8080', opt = {}) ⇒ Object

creates a new server, socket. address may be a HOST:PORT or an absolute path to a UNIX socket. address can even be a Socket object in which case it is immediately returned



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/unicorn/socket_helper.rb', line 108

def bind_listen(address = '0.0.0.0:8080', opt = {})
  return address unless String === address

  sock = if address[0] == ?/
    if File.exist?(address)
      if File.socket?(address)
        begin
          UNIXSocket.new(address).close
          # fall through, try to bind(2) and fail with EADDRINUSE
          # (or succeed from a small race condition we can't sanely avoid).
        rescue Errno::ECONNREFUSED
          logger.info "unlinking existing socket=#{address}"
          File.unlink(address)
        end
      else
        raise ArgumentError,
              "socket=#{address} specified but it is not a socket!"
      end
    end
    old_umask = File.umask(opt[:umask] || 0)
    begin
      Kgio::UNIXServer.new(address)
    ensure
      File.umask(old_umask)
    end
  elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/
    Kgio::TCPServer.new($1, $2.to_i)
  else
    raise ArgumentError, "Don't know how to bind: #{address}"
  end
  set_server_sockopt(sock, opt)
  sock
end

#log_buffer_sizes(sock, pfx = '') ⇒ Object



99
100
101
102
103
# File 'lib/unicorn/socket_helper.rb', line 99

def log_buffer_sizes(sock, pfx = '')
  rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
  sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
  logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
end

#server_cast(sock) ⇒ Object

casts a given Socket to be a TCPServer or UNIXServer



166
167
168
169
170
171
172
173
# File 'lib/unicorn/socket_helper.rb', line 166

def server_cast(sock)
  begin
    Socket.unpack_sockaddr_in(sock.getsockname)
    Kgio::TCPServer.for_fd(sock.fileno)
  rescue ArgumentError
    Kgio::UNIXServer.for_fd(sock.fileno)
  end
end

#set_server_sockopt(sock, opt) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/unicorn/socket_helper.rb', line 82

def set_server_sockopt(sock, opt)
  opt ||= {}

  TCPSocket === sock and set_tcp_sockopt(sock, opt)

  if opt[:rcvbuf] || opt[:sndbuf]
    log_buffer_sizes(sock, "before: ")
    sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
    sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
    log_buffer_sizes(sock, " after: ")
  end
  sock.listen(opt[:backlog] || 1024)
  rescue => e
    logger.error "error setting socket options: #{e.inspect}"
    logger.error e.backtrace.join("\n")
end

#set_tcp_sockopt(sock, opt) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/unicorn/socket_helper.rb', line 43

def set_tcp_sockopt(sock, opt)

  # highly portable, but off by default because we don't do keepalive
  if defined?(TCP_NODELAY) && ! (val = opt[:tcp_nodelay]).nil?
    sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
  end

  unless (val = opt[:tcp_nopush]).nil?
    val = val ? 1 : 0
    if defined?(TCP_CORK) # Linux
      sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
    elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
      sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
    end
  end

  # No good reason to ever have deferred accepts off
  # (except maybe benchmarking)
  if defined?(TCP_DEFER_ACCEPT)
    # this differs from nginx, since nginx doesn't allow us to
    # configure the the timeout...
    tmp = DEFAULTS.merge(opt)
    seconds = tmp[:tcp_defer_accept]
    seconds = DEFAULTS[:tcp_defer_accept] if seconds == true
    seconds = 0 unless seconds # nil/false means disable this
    sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
  elsif respond_to?(:accf_arg)
    tmp = DEFAULTS.merge(opt)
    if name = tmp[:accept_filter]
      begin
        sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
      rescue => e
        logger.error("#{sock_name(sock)} " \
                     "failed to set accept_filter=#{name} (#{e.inspect})")
      end
    end
  end
end