[前][次][番号順一覧][スレッド一覧]

ruby-changes:46435

From: shugo <ko1@a...>
Date: Wed, 3 May 2017 20:32:27 +0900 (JST)
Subject: [ruby-changes:46435] shugo:r58549 (trunk): net/imap: handle timeouts

shugo	2017-05-03 20:32:22 +0900 (Wed, 03 May 2017)

  New Revision: 58549

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=58549

  Log:
    net/imap: handle timeouts
    
    Patch by Pavel Rosick?\195?\189.  [Feature #13379] [ruby-core:80440]

  Modified files:
    trunk/lib/net/imap.rb
Index: lib/net/imap.rb
===================================================================
--- lib/net/imap.rb	(revision 58548)
+++ lib/net/imap.rb	(revision 58549)
@@ -18,6 +18,7 @@ require "socket" https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L18
 require "monitor"
 require "digest/md5"
 require "strscan"
+require 'net/protocol'
 begin
   require "openssl"
 rescue LoadError
@@ -199,7 +200,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L200
   #    Goldsmith, D. and Davis, M., "UTF-7: A Mail-Safe Transformation Format of
   #    Unicode", RFC 2152, May 1997.
   #
-  class IMAP
+  class IMAP < Protocol
     include MonitorMixin
     if defined?(OpenSSL::SSL)
       include OpenSSL
@@ -221,6 +222,16 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L222
     # Returns all response handlers.
     attr_reader :response_handlers
 
+    # Seconds to wait until a connection is opened.
+    # If the IMAP object cannot open a connection within this time,
+    # it raises a Net::OpenTimeout exception. The default value is 30 seconds.
+    attr_reader :open_timeout
+
+    # Seconds to wait until reading one block (by one read(1) call).
+    # If the IMAP object cannot complete a read() within this time,
+    # it raises a Net::ReadTimeout exception. The default value is 60 seconds.
+    attr_reader :read_timeout
+
     # The thread to receive exceptions.
     attr_accessor :client_thread
 
@@ -1048,6 +1059,8 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1059
     #         be installed.
     #         If options[:ssl] is a hash, it's passed to
     #         OpenSSL::SSL::SSLContext#set_params as parameters.
+    # open_timeout:: Seconds to wait until a connection is opened
+    # read_timeout:: Seconds to wait until reading one block
     #
     # The most common errors are:
     #
@@ -1076,8 +1089,10 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1089
       @port = options[:port] || (options[:ssl] ? SSL_PORT : PORT)
       @tag_prefix = "RUBY"
       @tagno = 0
+      @open_timeout = options[:open_timeout] || 30
+      @read_timeout = options[:read_timeout] || 60
       @parser = ResponseParser.new
-      @sock = TCPSocket.open(@host, @port)
+      @sock = tcp_socket(@host, @port)
       begin
         if options[:ssl]
           start_tls_session(options[:ssl])
@@ -1117,6 +1132,13 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1132
       end
     end
 
+    def tcp_socket(host, port)
+      Socket.tcp(host, port, :connect_timeout => @open_timeout)
+    rescue Errno::ETIMEDOUT
+      raise Net::OpenTimeout, "Timeout to open TCP connection to " +
+        "#{host}:#{port} (exceeds #{@open_timeout} seconds)"
+    end
+
     def receive_responses
       connection_closed = false
       until connection_closed
@@ -1199,14 +1221,35 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1221
       end
     end
 
+    def get_response_data(length, terminator = nil)
+      str = nil
+      buff = String.new
+      while true
+        str = @sock.read_nonblock(length, :exception => false)
+        case str
+        when :wait_readable
+          @sock.to_io.wait_readable(@read_timeout) or
+            raise Net::ReadTimeout, "#{@host}:#{@port} read timeout (exceeds #{@read_timeout} seconds)"
+        when nil
+          break
+        else
+          buff.concat(str)
+          if terminator ? buff.include?(terminator) : (buff.length >= length)
+            break
+          end
+        end
+      end
+      buff
+    end
+
     def get_response
       buff = String.new
       while true
-        s = @sock.gets(CRLF)
-        break unless s
+        s = get_response_data(1, CRLF)
+        break if s.length == 0
         buff.concat(s)
         if /\{(\d+)\}\r\n/n =~ s
-          s = @sock.read($1.to_i)
+          s = get_response_data($1.to_i)
           buff.concat(s)
         else
           break
@@ -1487,7 +1530,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1530
       end
       @sock = SSLSocket.new(@sock, context)
       @sock.sync_close = true
-      @sock.connect
+      ssl_socket_connect(@sock, @open_timeout)
       if context.verify_mode != VERIFY_NONE
         @sock.post_connection_check(@host)
       end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]