1 # -*- encoding: binary -*-
7 include Socket::Constants
9 # configure platform-specific options (only tested on Linux 2.6 so far)
12 # from /usr/include/linux/tcp.h
13 TCP_DEFER_ACCEPT = 9 unless defined?(TCP_DEFER_ACCEPT)
14 TCP_CORK = 3 unless defined?(TCP_CORK)
15 when /freebsd(([1-4]\..{1,2})|5\.[0-4])/
16 # Do nothing for httpready, just closing a bug when freebsd <= 5.4
17 TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
19 TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
20 # Use the HTTP accept filter if available.
21 # The struct made by pack() is defined in /usr/include/sys/socket.h
22 # as accept_filter_arg
23 unless `/sbin/sysctl -nq net.inet.accf.http`.empty?
24 FILTER_ARG = ['httpready', nil].pack('a16a240')
28 def set_tcp_sockopt(sock, opt)
30 # highly portable, but off by default because we don't do keepalive
31 if defined?(TCP_NODELAY) && ! (val = opt[:tcp_nodelay]).nil?
32 sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
35 unless (val = opt[:tcp_nopush]).nil?
37 if defined?(TCP_CORK) # Linux
38 sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
39 elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
40 sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
44 # No good reason to ever have deferred accepts off
45 if defined?(TCP_DEFER_ACCEPT)
46 sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, 1)
47 elsif defined?(SO_ACCEPTFILTER) && defined?(FILTER_ARG)
48 sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, FILTER_ARG)
52 def set_server_sockopt(sock, opt)
55 TCPSocket === sock and set_tcp_sockopt(sock, opt)
57 if opt[:rcvbuf] || opt[:sndbuf]
58 log_buffer_sizes(sock, "before: ")
59 sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
60 sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
61 log_buffer_sizes(sock, " after: ")
63 sock.listen(opt[:backlog] || 1024)
65 if respond_to?(:logger)
66 logger.error "error setting socket options: #{e.inspect}"
67 logger.error e.backtrace.join("\n")
71 def log_buffer_sizes(sock, pfx = '')
72 respond_to?(:logger) or return
73 rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
74 sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
75 logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
78 # creates a new server, socket. address may be a HOST:PORT or
79 # an absolute path to a UNIX socket. address can even be a Socket
80 # object in which case it is immediately returned
81 def bind_listen(address = '0.0.0.0:8080', opt = {})
82 return address unless String === address
84 sock = if address[0] == ?/
85 if File.exist?(address)
86 if File.socket?(address)
87 if self.respond_to?(:logger)
88 logger.info "unlinking existing socket=#{address}"
93 "socket=#{address} specified but it is not a socket!"
96 old_umask = File.umask(opt[:umask] || 0)
98 UNIXServer.new(address)
100 File.umask(old_umask)
102 elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/
103 TCPServer.new($1, $2.to_i)
105 raise ArgumentError, "Don't know how to bind: #{address}"
107 set_server_sockopt(sock, opt)
111 # Returns the configuration name of a socket as a string. sock may
112 # be a string value, in which case it is returned as-is
113 # Warning: TCP sockets may not always return the name given to it.
116 when String then sock
118 Socket.unpack_sockaddr_un(sock.getsockname)
120 Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
123 Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
125 Socket.unpack_sockaddr_un(sock.getsockname)
128 raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
132 # casts a given Socket to be a TCPServer or UNIXServer
133 def server_cast(sock)
135 Socket.unpack_sockaddr_in(sock.getsockname)
136 TCPServer.for_fd(sock.fileno)
138 UNIXServer.for_fd(sock.fileno)
142 end # module SocketHelper