ruby-changes:46678
From: shugo <ko1@a...>
Date: Fri, 19 May 2017 18:25:57 +0900 (JST)
Subject: [ruby-changes:46678] shugo:r58792 (trunk): net/imap: Net::IMAP#append should not block when NO response is received
shugo 2017-05-19 18:25:52 +0900 (Fri, 19 May 2017) New Revision: 58792 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=58792 Log: net/imap: Net::IMAP#append should not block when NO response is received [ruby-dev:50129] [Bug#13579] Modified files: trunk/lib/net/imap.rb trunk/test/net/imap/test_imap.rb Index: lib/net/imap.rb =================================================================== --- lib/net/imap.rb (revision 58791) +++ lib/net/imap.rb (revision 58792) @@ -1098,6 +1098,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1098 @tagged_responses = {} @response_handlers = [] @tagged_response_arrival = new_cond + @continued_command_tag = nil @continuation_request_arrival = new_cond @idle_done_cond = nil @logout_command_tag = nil @@ -1160,8 +1161,12 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1161 when TaggedResponse @tagged_responses[resp.tag] = resp @tagged_response_arrival.broadcast - if resp.tag == @logout_command_tag + case resp.tag + when @logout_command_tag return + when @continued_command_tag + @exception = RESPONSE_ERRORS[resp.name].new(resp) + @continuation_request_arrival.signal end when UntaggedResponse record_response(resp.name, resp.data) @@ -1251,7 +1256,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1256 put_string(tag + " " + cmd) args.each do |i| put_string(" ") - send_data(i) + send_data(i, tag) end put_string(CRLF) if cmd == "LOGOUT" @@ -1307,7 +1312,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1312 end end - def send_data(data) + def send_data(data, tag = nil) case data when nil put_string("NIL") @@ -1322,7 +1327,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1327 when Symbol send_symbol_data(data) else - data.send_data(self) + data.send_data(self, tag) end end @@ -1345,11 +1350,16 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1350 put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"') end - def send_literal(str) + def send_literal(str, tag) put_string("{" + str.bytesize.to_s + "}" + CRLF) - @continuation_request_arrival.wait - raise @exception if @exception - put_string(str) + @continued_command_tag = tag + begin + @continuation_request_arrival.wait + raise @exception if @exception + put_string(str) + ensure + @continued_command_tag = nil + end end def send_number_data(num) @@ -1510,7 +1520,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1520 end class RawData # :nodoc: - def send_data(imap) + def send_data(imap, tag) imap.send(:put_string, @data) end @@ -1525,7 +1535,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1535 end class Atom # :nodoc: - def send_data(imap) + def send_data(imap, tag) imap.send(:put_string, @data) end @@ -1540,7 +1550,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1550 end class QuotedString # :nodoc: - def send_data(imap) + def send_data(imap, tag) imap.send(:send_quoted_string, @data) end @@ -1555,8 +1565,8 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1565 end class Literal # :nodoc: - def send_data(imap) - imap.send(:send_literal, @data) + def send_data(imap, tag) + imap.send(:send_literal, @data, tag) end def validate @@ -1570,7 +1580,7 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L1580 end class MessageSet # :nodoc: - def send_data(imap) + def send_data(imap, tag) imap.send(:put_string, format_internal(@data)) end @@ -3653,6 +3663,10 @@ module Net https://github.com/ruby/ruby/blob/trunk/lib/net/imap.rb#L3663 # out due to inactivity. class ByeResponseError < ResponseError end + + RESPONSE_ERRORS = Hash.new(ResponseError) + RESPONSE_ERRORS["NO"] = NoResponseError + RESPONSE_ERRORS["BAD"] = BadResponseError # Error raised when too many flags are interned to symbols. class FlagCountError < Error Index: test/net/imap/test_imap.rb =================================================================== --- test/net/imap/test_imap.rb (revision 58791) +++ test/net/imap/test_imap.rb (revision 58792) @@ -559,6 +559,91 @@ class IMAPTest < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/net/imap/test_imap.rb#L559 end end + def test_append + server = create_tcp_server + port = server.addr[1] + mail = <<EOF.gsub(/\n/, "\r\n") +From: shugo@e... +To: matz@e... +Subject: hello + +hello world +EOF + requests = [] + received_mail = nil + @threads << Thread.start do + sock = server.accept + begin + sock.print("* OK test server\r\n") + line = sock.gets + requests.push(line) + size = line.slice(/{(\d+)}\r\n/, 1).to_i + sock.print("+ Ready for literal data\r\n") + received_mail = sock.read(size) + sock.gets + sock.print("RUBY0001 OK APPEND completed\r\n") + sock.gets + sock.print("* BYE terminating connection\r\n") + sock.print("RUBY0002 OK LOGOUT completed\r\n") + ensure + sock.close + server.close + end + end + + begin + imap = Net::IMAP.new(SERVER_ADDR, :port => port) + resp = imap.append("INBOX", mail) + assert_equal(1, requests.length) + assert_equal("RUBY0001 APPEND INBOX {#{mail.size}}\r\n", requests[0]) + assert_equal(mail, received_mail) + imap.logout + ensure + imap.disconnect if imap + end + end + + def test_append_fail + server = create_tcp_server + port = server.addr[1] + mail = <<EOF.gsub(/\n/, "\r\n") +From: shugo@e... +To: matz@e... +Subject: hello + +hello world +EOF + requests = [] + received_mail = nil + @threads << Thread.start do + sock = server.accept + begin + sock.print("* OK test server\r\n") + line = sock.gets + requests.push(line) + sock.print("RUBY0001 NO Mailbox doesn't exist\r\n") + sock.gets + sock.print("* BYE terminating connection\r\n") + sock.print("RUBY0002 OK LOGOUT completed\r\n") + ensure + sock.close + server.close + end + end + + begin + imap = Net::IMAP.new(SERVER_ADDR, :port => port) + assert_raise(Net::IMAP::NoResponseError) do + imap.append("INBOX", mail) + end + assert_equal(1, requests.length) + assert_equal("RUBY0001 APPEND INBOX {#{mail.size}}\r\n", requests[0]) + imap.logout + ensure + imap.disconnect if imap + end + end + private def imaps_test -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/