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)
15 # do not send out partial frames (Linux)
16 TCP_CORK = 3 unless defined?(TCP_CORK)
17 when /freebsd(([1-4]\..{1,2})|5\.[0-4])/
18 # Do nothing for httpready, just closing a bug when freebsd <= 5.4
19 TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH) # :nodoc:
21 # do not send out partial frames (FreeBSD)
22 TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
24 # Use the HTTP accept filter if available.
25 # The struct made by pack() is defined in /usr/include/sys/socket.h
26 # as accept_filter_arg
27 unless `/sbin/sysctl -nq net.inet.accf.http`.empty?
28 # set set the "httpready" accept filter in FreeBSD if available
29 # if other protocols are to be supported, this may be
30 # String#replace-d with "dataready" arguments instead
31 FILTER_ARG = ['httpready', nil].pack('a16a240')
35 def set_tcp_sockopt(sock, opt)
37 # highly portable, but off by default because we don't do keepalive
38 if defined?(TCP_NODELAY) && ! (val = opt[:tcp_nodelay]).nil?
39 sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
42 unless (val = opt[:tcp_nopush]).nil?
44 if defined?(TCP_CORK) # Linux
45 sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
46 elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
47 sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
51 # No good reason to ever have deferred accepts off
52 if defined?(TCP_DEFER_ACCEPT)
53 sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, 1)
54 elsif defined?(SO_ACCEPTFILTER) && defined?(FILTER_ARG)
55 sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, FILTER_ARG)
59 def set_server_sockopt(sock, opt)
62 TCPSocket === sock and set_tcp_sockopt(sock, opt)
64 if opt[:rcvbuf] || opt[:sndbuf]
65 log_buffer_sizes(sock, "before: ")
66 sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
67 sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
68 log_buffer_sizes(sock, " after: ")
70 sock.listen(opt[:backlog] || 1024)
72 if respond_to?(:logger)
73 logger.error "error setting socket options: #{e.inspect}"
74 logger.error e.backtrace.join("\n")
78 def log_buffer_sizes(sock, pfx = '')
79 respond_to?(:logger) or return
80 rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
81 sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
82 logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
85 # creates a new server, socket. address may be a HOST:PORT or
86 # an absolute path to a UNIX socket. address can even be a Socket
87 # object in which case it is immediately returned
88 def bind_listen(address = '0.0.0.0:8080', opt = {})
89 return address unless String === address
91 sock = if address[0] == ?/
92 if File.exist?(address)
93 if File.socket?(address)
94 if self.respond_to?(:logger)
95 logger.info "unlinking existing socket=#{address}"
100 "socket=#{address} specified but it is not a socket!"
103 old_umask = File.umask(opt[:umask] || 0)
105 UNIXServer.new(address)
107 File.umask(old_umask)
109 elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/
110 TCPServer.new($1, $2.to_i)
112 raise ArgumentError, "Don't know how to bind: #{address}"
114 set_server_sockopt(sock, opt)
118 # Returns the configuration name of a socket as a string. sock may
119 # be a string value, in which case it is returned as-is
120 # Warning: TCP sockets may not always return the name given to it.
123 when String then sock
125 Socket.unpack_sockaddr_un(sock.getsockname)
127 Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
130 Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
132 Socket.unpack_sockaddr_un(sock.getsockname)
135 raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
139 module_function :sock_name
141 # casts a given Socket to be a TCPServer or UNIXServer
142 def server_cast(sock)
144 Socket.unpack_sockaddr_in(sock.getsockname)
145 TCPServer.for_fd(sock.fileno)
147 UNIXServer.for_fd(sock.fileno)
151 end # module SocketHelper