Save one fcntl() syscall on every request
[unicorn.git] / lib / unicorn / socket.rb
blob5a3bfd7513688861bde81e7c8195be7a43ee0d99
1 require 'socket'
3 class UNIXSocket
4   UNICORN_PEERADDR = '127.0.0.1'.freeze
5   def unicorn_peeraddr
6     UNICORN_PEERADDR
7   end
8 end
10 class TCPSocket
11   def unicorn_peeraddr
12     peeraddr.last
13   end
14 end
16 module Unicorn
17   module SocketHelper
18     include Socket::Constants
20     def set_server_sockopt(sock, opt)
21       opt ||= {}
22       if opt[:rcvbuf] || opt[:sndbuf]
23         log_buffer_sizes(sock, "before: ")
24         sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
25         sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
26         log_buffer_sizes(sock, " after: ")
27       end
28       sock.listen(opt[:backlog] || 1024)
29     end
31     def log_buffer_sizes(sock, pfx = '')
32       respond_to?(:logger) or return
33       rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
34       sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
35       logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
36     end
38     # creates a new server, socket. address may be a HOST:PORT or
39     # an absolute path to a UNIX socket.  address can even be a Socket
40     # object in which case it is immediately returned
41     def bind_listen(address = '0.0.0.0:8080', opt = { :backlog => 1024 })
42       return address unless String === address
44       sock = if address[0..0] == "/"
45         if File.exist?(address)
46           if File.socket?(address)
47             if self.respond_to?(:logger)
48               logger.info "unlinking existing socket=#{address}"
49             end
50             File.unlink(address)
51           else
52             raise ArgumentError,
53                   "socket=#{address} specified but it is not a socket!"
54           end
55         end
56         old_umask = File.umask(0)
57         begin
58           UNIXServer.new(address)
59         ensure
60           File.umask(old_umask)
61         end
62       elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/
63         TCPServer.new($1, $2.to_i)
64       else
65         raise ArgumentError, "Don't know how to bind: #{address}"
66       end
67       set_server_sockopt(sock, opt)
68       sock
69     end
71     # Returns the configuration name of a socket as a string.  sock may
72     # be a string value, in which case it is returned as-is
73     # Warning: TCP sockets may not always return the name given to it.
74     def sock_name(sock)
75       case sock
76       when String then sock
77       when UNIXServer
78         Socket.unpack_sockaddr_un(sock.getsockname)
79       when TCPServer
80         Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
81       when Socket
82         begin
83           Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
84         rescue ArgumentError
85           Socket.unpack_sockaddr_un(sock.getsockname)
86         end
87       else
88         raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
89       end
90     end
92     # casts a given Socket to be a TCPServer or UNIXServer
93     def server_cast(sock)
94       begin
95         Socket.unpack_sockaddr_in(sock.getsockname)
96         TCPServer.for_fd(sock.fileno)
97       rescue ArgumentError
98         UNIXServer.for_fd(sock.fileno)
99       end
100     end
102   end # module SocketHelper
103 end # module Unicorn