1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

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
This commit is contained in:
shugo 2017-05-19 09:25:52 +00:00
parent 3c5344bf30
commit 84c1596342
2 changed files with 113 additions and 14 deletions

View file

@ -1098,6 +1098,7 @@ module Net
@tagged_responses = {} @tagged_responses = {}
@response_handlers = [] @response_handlers = []
@tagged_response_arrival = new_cond @tagged_response_arrival = new_cond
@continued_command_tag = nil
@continuation_request_arrival = new_cond @continuation_request_arrival = new_cond
@idle_done_cond = nil @idle_done_cond = nil
@logout_command_tag = nil @logout_command_tag = nil
@ -1160,8 +1161,12 @@ module Net
when TaggedResponse when TaggedResponse
@tagged_responses[resp.tag] = resp @tagged_responses[resp.tag] = resp
@tagged_response_arrival.broadcast @tagged_response_arrival.broadcast
if resp.tag == @logout_command_tag case resp.tag
when @logout_command_tag
return return
when @continued_command_tag
@exception = RESPONSE_ERRORS[resp.name].new(resp)
@continuation_request_arrival.signal
end end
when UntaggedResponse when UntaggedResponse
record_response(resp.name, resp.data) record_response(resp.name, resp.data)
@ -1251,7 +1256,7 @@ module Net
put_string(tag + " " + cmd) put_string(tag + " " + cmd)
args.each do |i| args.each do |i|
put_string(" ") put_string(" ")
send_data(i) send_data(i, tag)
end end
put_string(CRLF) put_string(CRLF)
if cmd == "LOGOUT" if cmd == "LOGOUT"
@ -1307,7 +1312,7 @@ module Net
end end
end end
def send_data(data) def send_data(data, tag = nil)
case data case data
when nil when nil
put_string("NIL") put_string("NIL")
@ -1322,7 +1327,7 @@ module Net
when Symbol when Symbol
send_symbol_data(data) send_symbol_data(data)
else else
data.send_data(self) data.send_data(self, tag)
end end
end end
@ -1345,11 +1350,16 @@ module Net
put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"') put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"')
end end
def send_literal(str) def send_literal(str, tag)
put_string("{" + str.bytesize.to_s + "}" + CRLF) put_string("{" + str.bytesize.to_s + "}" + CRLF)
@continuation_request_arrival.wait @continued_command_tag = tag
raise @exception if @exception begin
put_string(str) @continuation_request_arrival.wait
raise @exception if @exception
put_string(str)
ensure
@continued_command_tag = nil
end
end end
def send_number_data(num) def send_number_data(num)
@ -1510,7 +1520,7 @@ module Net
end end
class RawData # :nodoc: class RawData # :nodoc:
def send_data(imap) def send_data(imap, tag)
imap.send(:put_string, @data) imap.send(:put_string, @data)
end end
@ -1525,7 +1535,7 @@ module Net
end end
class Atom # :nodoc: class Atom # :nodoc:
def send_data(imap) def send_data(imap, tag)
imap.send(:put_string, @data) imap.send(:put_string, @data)
end end
@ -1540,7 +1550,7 @@ module Net
end end
class QuotedString # :nodoc: class QuotedString # :nodoc:
def send_data(imap) def send_data(imap, tag)
imap.send(:send_quoted_string, @data) imap.send(:send_quoted_string, @data)
end end
@ -1555,8 +1565,8 @@ module Net
end end
class Literal # :nodoc: class Literal # :nodoc:
def send_data(imap) def send_data(imap, tag)
imap.send(:send_literal, @data) imap.send(:send_literal, @data, tag)
end end
def validate def validate
@ -1570,7 +1580,7 @@ module Net
end end
class MessageSet # :nodoc: class MessageSet # :nodoc:
def send_data(imap) def send_data(imap, tag)
imap.send(:put_string, format_internal(@data)) imap.send(:put_string, format_internal(@data))
end end
@ -3653,6 +3663,10 @@ module Net
# out due to inactivity. # out due to inactivity.
class ByeResponseError < ResponseError class ByeResponseError < ResponseError
end end
RESPONSE_ERRORS = Hash.new(ResponseError)
RESPONSE_ERRORS["NO"] = NoResponseError
RESPONSE_ERRORS["BAD"] = BadResponseError
# Error raised when too many flags are interned to symbols. # Error raised when too many flags are interned to symbols.
class FlagCountError < Error class FlagCountError < Error

View file

@ -559,6 +559,91 @@ class IMAPTest < Test::Unit::TestCase
end end
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 private
def imaps_test def imaps_test