Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions test/net/imap/fake_server/command_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
require "net/imap"

class Net::IMAP::FakeServer
CommandParseError = RuntimeError
CommandParseError = Class.new(RuntimeError)

class CommandReader
attr_reader :config
attr_reader :last_command
attr_accessor :literal_acceptor

def initialize(socket)
def initialize(socket, config:)
@config = config
@socket = socket
@last_command = nil
@literal_acceptor = proc {|buff, size| true }
Expand All @@ -35,8 +37,11 @@ def get_command
end
throw :eof if buf.empty?
@last_command = parse(buf)
rescue CommandParseError => err
raise IOError, err.message if socket.eof? && !buf.end_with?("\r\n")
rescue CommandParseError
if config.ignore_abrupt_eof? && socket.eof? && !buf.end_with?("\r\n")
throw :eof
end
raise
end

private
Expand All @@ -46,7 +51,7 @@ def get_command
# TODO: convert bad command exception to tagged BAD response, when possible
def parse(buf)
/\A([^ ]+) ((?:UID )?\w+)(?: (.+))?\r\n\z/min =~ buf or
raise CommandParseError, "bad request: %p" [buf]
raise CommandParseError, "bad request: %p" % [buf]
case $2.upcase
when "LOGIN", "SELECT", "EXAMINE", "ENABLE", "AUTHENTICATE"
Command.new $1, $2, scan_astrings($3), buf
Expand Down
3 changes: 3 additions & 0 deletions test/net/imap/fake_server/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class Configuration
mailboxes: {
"INBOX" => { name: "INBOX" }.freeze,
}.freeze,

ignore_abrupt_eof: false,
}

def initialize(with_extensions: [], without_extensions: [], **opts, &block)
Expand All @@ -68,6 +70,7 @@ def initialize(with_extensions: [], without_extensions: [], **opts, &block)
alias greeting_bye? greeting_bye
alias greeting_capabilities? greeting_capabilities
alias sasl_ir? sasl_ir
alias ignore_abrupt_eof? ignore_abrupt_eof

def on(event, &handler)
handler or raise ArgumentError
Expand Down
2 changes: 1 addition & 1 deletion test/net/imap/fake_server/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def initialize(server, tcp_socket:)
@config = server.config
@socket = Socket.new tcp_socket, config: config
@state = ConnectionState.new socket: socket, config: config
@reader = CommandReader.new socket
@reader = CommandReader.new socket, config: config
@writer = ResponseWriter.new socket, config: config, state: state
@router = CommandRouter.new writer, config: config, state: state
@mutex = Thread::Mutex.new
Expand Down
16 changes: 12 additions & 4 deletions test/net/imap/fake_server/socket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ def initialize(tcp_socket, config:)
def tls?; !!@tls_socket end
def closed?; @closed end

def eof?; socket.eof? end
def gets(...) socket.gets(...) end
def read(...) socket.read(...) end
def print(...) socket.print(...) end
def eof?; ignore_closed?(true) { socket.eof? } end
def gets(...) ignore_closed?(nil) { socket.gets(...) } end
def read(...) ignore_closed?(nil) { socket.read(...) } end
def print(...) ignore_closed?(nil) { socket.print(...) } end

def use_tls
@tls_socket ||= OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_ctx).tap do |s|
Expand All @@ -48,5 +48,13 @@ def ssl_ctx
end
end

def ignore_closed?(fallback)
yield
rescue IOError => err
close if !closed? && (@tcp_socket.closed? || @tls_socket.closed?)
return fallback if err.message.match?(/stream closed|closed stream/i)
raise
end

end
end
Loading