listen backlog, sndbuf, rcvbuf are always changeable
[unicorn.git] / lib / unicorn / socket.rb
blob4cb8d48d336beeb3ba56bb1ddd93fd5f8a7e5673
1 require 'socket'
2 require 'io/nonblock'
4 # non-portable Socket code goes here:
5 class Socket
6   module Constants
7     # configure platform-specific options (only tested on Linux 2.6 so far)
8     case RUBY_PLATFORM
9     when /linux/
10       # from /usr/include/linux/tcp.h
11       TCP_DEFER_ACCEPT = 9 unless defined?(TCP_DEFER_ACCEPT)
12       TCP_CORK = 3 unless defined?(TCP_CORK)
13     when /freebsd(([1-4]\..{1,2})|5\.[0-4])/
14     when /freebsd/
15       # Use the HTTP accept filter if available.
16       # The struct made by pack() is defined in /usr/include/sys/socket.h
17       # as accept_filter_arg
18       unless `/sbin/sysctl -nq net.inet.accf.http`.empty?
19         unless defined?(SO_ACCEPTFILTER_HTTPREADY)
20           SO_ACCEPTFILTER_HTTPREADY = ['httpready',nil].pack('a16a240').freeze
21         end
23       end
24     end
25   end
26 end
28 class UNIXSocket
29   UNICORN_PEERADDR = '127.0.0.1'.freeze
30   def unicorn_peeraddr
31     UNICORN_PEERADDR
32   end
33 end
35 class TCPSocket
36   def unicorn_peeraddr
37     peeraddr.last
38   end
39 end
41 module Unicorn
42   module SocketHelper
43     include Socket::Constants
45     def set_client_sockopt(sock)
46       sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) if defined?(TCP_NODELAY)
47       sock.setsockopt(SOL_TCP, TCP_CORK, 1) if defined?(TCP_CORK)
48     end
50     def set_server_sockopt(sock, opt)
51       opt ||= {}
52       if opt[:rcvbuf] || opt[:sndbuf]
53         log_buffer_sizes(sock, "before: ")
54         sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
55         sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
56         log_buffer_sizes(sock, " after: ")
57       end
58       sock.listen(opt[:backlog] || 1024)
59       return if sock_name(sock)[0..0] == "/"
61       if defined?(TCP_DEFER_ACCEPT)
62         sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, 1) rescue nil
63       end
64       if defined?(SO_ACCEPTFILTER_HTTPREADY)
65         sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER,
66                         SO_ACCEPTFILTER_HTTPREADY) rescue nil
67       end
68     end
70     def log_buffer_sizes(sock, pfx = '')
71       respond_to?(:logger) or return
72       rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
73       sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
74       logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
75     end
77     # creates a new server, socket. address may be a HOST:PORT or
78     # an absolute path to a UNIX socket.  address can even be a Socket
79     # object in which case it is immediately returned
80     def bind_listen(address = '0.0.0.0:8080', opt = { :backlog => 1024 })
81       return address unless String === address
83       sock = if address[0..0] == "/"
84         if File.exist?(address)
85           if File.socket?(address)
86             if self.respond_to?(:logger)
87               logger.info "unlinking existing socket=#{address}"
88             end
89             File.unlink(address)
90           else
91             raise ArgumentError,
92                   "socket=#{address} specified but it is not a socket!"
93           end
94         end
95         old_umask = File.umask(0)
96         begin
97           UNIXServer.new(address)
98         ensure
99           File.umask(old_umask)
100         end
101       elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/
102         TCPServer.new($1, $2.to_i)
103       else
104         raise ArgumentError, "Don't know how to bind: #{address}"
105       end
106       set_server_sockopt(sock, opt)
107       sock
108     end
110     # Returns the configuration name of a socket as a string.  sock may
111     # be a string value, in which case it is returned as-is
112     # Warning: TCP sockets may not always return the name given to it.
113     def sock_name(sock)
114       case sock
115       when String then sock
116       when UNIXServer
117         Socket.unpack_sockaddr_un(sock.getsockname)
118       when TCPServer
119         Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
120       when Socket
121         begin
122           Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
123         rescue ArgumentError
124           Socket.unpack_sockaddr_un(sock.getsockname)
125         end
126       else
127         raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
128       end
129     end
131     # casts a given Socket to be a TCPServer or UNIXServer
132     def server_cast(sock)
133       begin
134         Socket.unpack_sockaddr_in(sock.getsockname)
135         TCPServer.for_fd(sock.fileno)
136       rescue ArgumentError
137         UNIXServer.for_fd(sock.fileno)
138       end
139     end
141   end # module SocketHelper
142 end # module Unicorn