socket/pure_ruby: connect with "exception:false" on Ruby 2.3+
[ruby-mogilefs-client.git] / lib / mogilefs / socket / pure_ruby.rb
blob59e43e1ebd95c0c2778c0804526d18fe1a33c2e1
1 # -*- encoding: binary -*-
2 # internal implementation details here, do not rely on them in your code
3 require 'io/wait'
5 class MogileFS::Socket < Socket
6   include MogileFS::SocketCommon
8   if RUBY_VERSION.to_f >= 2.3
9     def self.start(host, port)
10       sock = new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
11       sock.connect_nonblock(sockaddr_in(port, host), :exception => false)
12       sock.post_init(host, port)
13     end
14   else
15     def self.start(host, port)
16       sock = new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
17       begin
18         sock.connect_nonblock(sockaddr_in(port, host))
19       rescue Errno::EINPROGRESS
20       end
21       sock.post_init(host, port)
22     end
23   end
25   def self.tcp(host, port, timeout = 5)
26     sock = start(host, port)
27     unless sock.wait_writable(timeout)
28       sock.close
29       raise MogileFS::Timeout, 'socket connect timeout'
30     end
31     sock
32   end
34   def wait_writable(timeout = nil)
35     IO.select(nil, [ self ], nil, timeout)
36   end unless self.instance_methods.include?(:wait_writable) # Ruby <2.0.0
38   def timed_peek(len, dst, timeout = 5)
39     begin
40       rc = recv_nonblock(len, Socket::MSG_PEEK)
41       return rc.empty? ? nil : dst.replace(rc)
42     rescue Errno::EAGAIN
43       wait(timeout) or unreadable_socket!(timeout)
44     rescue EOFError
45       dst.replace("")
46       return
47     end while true
48   rescue EOFError
49     nil
50   end
52   # write_nonblock and read_nonblock support `exception: false`
53   if RUBY_VERSION.to_f >= 2.1
54     def timed_read(len, dst = "", timeout = 5)
55       case rc = read_nonblock(len, dst, :exception => false)
56       when String
57         return rc
58       when :wait_readable
59         wait(timeout) or unreadable_socket!(timeout)
60       when nil
61         return
62       end while true
63     rescue EOFError
64       nil
65     end
67     def timed_write(buf, timeout = 5)
68       written = 0
69       expect = buf.bytesize
70       case rc = write_nonblock(buf, :exception => false)
71       when Integer
72         return expect if rc == buf.bytesize
73         written += rc
74         buf = buf.byteslice(rc, buf.bytesize) # Ruby 1.9.3+
75       when :wait_writable
76         wait_writable(timeout) or request_truncated!(written, expect, timeout)
77       end while true
78     end
79   else # Ruby 1.8.7 - 2.0.0
80     def timed_read(len, dst = "", timeout = 5)
81       begin
82         return read_nonblock(len, dst)
83       rescue Errno::EAGAIN
84         wait(timeout) or unreadable_socket!(timeout)
85       rescue EOFError
86         return
87       end while true
88     rescue EOFError
89       nil
90     end
92     def timed_write(buf, timeout = 5)
93       written = 0
94       expect = buf.bytesize
95       begin
96         rc = write_nonblock(buf)
97         return expect if rc == buf.bytesize
98         written += rc
100         if buf.respond_to?(:byteslice) # Ruby 1.9.3+
101           buf = buf.byteslice(rc, buf.bytesize)
102         else
103           if buf.respond_to?(:encoding) && buf.encoding != Encoding::BINARY
104             buf = buf.dup.force_encoding(Encoding::BINARY)
105           end
106           buf = buf.slice(rc, buf.bytesize)
107         end
108       rescue Errno::EAGAIN
109         wait_writable(timeout) or request_truncated!(written, expect, timeout)
110       end while true
111     end
112   end