aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshugo <shugo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-05-19 09:25:52 +0000
committershugo <shugo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-05-19 09:25:52 +0000
commit05a85daa12db280481634cb62b9470fad6376d0f (patch)
tree70557ddb520309889555c48e891a4d6de0067681
parentdc516c5235149f056aa2809a2c1609aaf4cfa84f (diff)
downloadruby-05a85daa12db280481634cb62b9470fad6376d0f.tar.gz
net/imap: Net::IMAP#append should not block when NO response is received
[ruby-dev:50129] [Bug#13579] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58792 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--lib/net/imap.rb42
-rw-r--r--test/net/imap/test_imap.rb85
2 files changed, 113 insertions, 14 deletions
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
index e64f48070d..5c11a9bedb 100644
--- a/lib/net/imap.rb
+++ b/lib/net/imap.rb
@@ -1098,6 +1098,7 @@ module Net
@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
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
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
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
when Symbol
send_symbol_data(data)
else
- data.send_data(self)
+ data.send_data(self, tag)
end
end
@@ -1345,11 +1350,16 @@ module Net
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
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
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
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
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
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
# 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
diff --git a/test/net/imap/test_imap.rb b/test/net/imap/test_imap.rb
index 9266cbdf81..8659622554 100644
--- a/test/net/imap/test_imap.rb
+++ b/test/net/imap/test_imap.rb
@@ -559,6 +559,91 @@ class IMAPTest < Test::Unit::TestCase
end
end
+ def test_append
+ server = create_tcp_server
+ port = server.addr[1]
+ mail = <<EOF.gsub(/\n/, "\r\n")
+From: shugo@example.com
+To: matz@example.com
+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@example.com
+To: matz@example.com
+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