Module: Yahns::SocketHelper
- Included in:
- Server
- Defined in:
- lib/yahns/socket_helper.rb
Overview
Copyright © 2013, Eric Wong <[email protected]> and all contributors License: GPLv3 or later (www.gnu.org/licenses/gpl-3.0.txt) this is only meant for Yahns::Server
Instance Method Summary collapse
-
#bind_listen(address, opt) ⇒ Object
creates a new server, socket.
- #log_buffer_sizes(sock, pfx = '') ⇒ Object
- #new_tcp_server(addr, port, opt) ⇒ Object
-
#server_cast(sock) ⇒ Object
casts a given Socket to be a TCPServer or UNIXServer.
- #set_server_sockopt(sock, opt) ⇒ Object
-
#so_reuseport ⇒ Object
Linux got SO_REUSEPORT in 3.9, BSDs have had it for ages.
-
#sock_name(sock) ⇒ Object
Returns the configuration name of a socket as a string.
-
#tcp_name(sock) ⇒ Object
returns rfc2732-style (e.g. “[::1]:666”) addresses for IPv6.
Instance Method Details
#bind_listen(address, 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
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 81 82 83 84 85 86 |
# File 'lib/yahns/socket_helper.rb', line 51 def bind_listen(address, opt) return address unless String === address opt ||= {} 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 Yahns::UNIXServer.new(address) ensure File.umask(old_umask) end elsif /\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address new_tcp_server($1, $2.to_i, opt.merge(ipv6: true)) elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address new_tcp_server($1, $2.to_i, opt) else raise ArgumentError, "Don't know how to bind: #{address}" end set_server_sockopt(sock, opt) sock end |
#log_buffer_sizes(sock, pfx = '') ⇒ Object
41 42 43 44 45 46 |
# File 'lib/yahns/socket_helper.rb', line 41 def log_buffer_sizes(sock, pfx = '') rcvbuf = sock.getsockopt(:SOL_SOCKET, :SO_RCVBUF).int sndbuf = sock.getsockopt(:SOL_SOCKET, :SO_SNDBUF).int @logger.info("#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}") rescue # TODO: get this fixed in rbx end |
#new_tcp_server(addr, port, opt) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/yahns/socket_helper.rb', line 88 def new_tcp_server(addr, port, opt) sock = Socket.new(opt[:ipv6] ? :INET6 : :INET, :STREAM, 0) if opt.key?(:ipv6only) sock.setsockopt(:IPPROTO_IPV6, :IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0) end sock.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1) begin sock.setsockopt(:SOL_SOCKET, so_reuseport, 1) rescue => e name = sock_name(sock) @logger.warn("failed to set SO_REUSEPORT on #{name}: #{e.}") end if opt[:reuseport] sock.bind(Socket.pack_sockaddr_in(port, addr)) sock.autoclose = false Yahns::TCPServer.for_fd(sock.fileno) end |
#server_cast(sock) ⇒ Object
casts a given Socket to be a TCPServer or UNIXServer
135 136 137 138 139 140 141 142 143 |
# File 'lib/yahns/socket_helper.rb', line 135 def server_cast(sock) sock.autoclose = false begin Socket.unpack_sockaddr_in(sock.getsockname) Yahns::TCPServer.for_fd(sock.fileno) rescue ArgumentError Yahns::UNIXServer.for_fd(sock.fileno) end end |
#set_server_sockopt(sock, opt) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/yahns/socket_helper.rb', line 22 def set_server_sockopt(sock, opt) opt = {backlog: 1024}.merge!(opt || {}) sock.close_on_exec = true TCPSocket === sock and sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, 1) sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1) if opt[:rcvbuf] || opt[:sndbuf] log_buffer_sizes(sock, "before: ") { SO_RCVBUF: :rcvbuf, SO_SNDBUF: :sndbuf }.each do |optname,cfgname| val = opt[cfgname] and sock.setsockopt(:SOL_SOCKET, optname, val) end log_buffer_sizes(sock, " after: ") end sock.listen(opt[:backlog]) rescue => e Yahns::Log.exception(@logger, "#{sock_name(sock)} #{opt.inspect}", e) end |
#so_reuseport ⇒ Object
Linux got SO_REUSEPORT in 3.9, BSDs have had it for ages
8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/yahns/socket_helper.rb', line 8 def so_reuseport if defined?(Socket::SO_REUSEPORT) Socket::SO_REUSEPORT elsif RUBY_PLATFORM =~ /linux/ if RUBY_PLATFORM =~ /(?:alpha|mips|parisc|sparc)/ 0x0200 # untested else 15 # only tested on x86_64 and i686 end else nil end end |
#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.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/yahns/socket_helper.rb', line 116 def sock_name(sock) case sock when String then sock when UNIXServer Socket.unpack_sockaddr_un(sock.getsockname) when TCPServer tcp_name(sock) when Socket begin tcp_name(sock) rescue ArgumentError Socket.unpack_sockaddr_un(sock.getsockname) end else raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}" end end |
#tcp_name(sock) ⇒ Object
returns rfc2732-style (e.g. “[::1]:666”) addresses for IPv6
108 109 110 111 |
# File 'lib/yahns/socket_helper.rb', line 108 def tcp_name(sock) port, addr = Socket.unpack_sockaddr_in(sock.getsockname) /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}" end |