summaryrefslogtreecommitdiff
path: root/jni/ruby/test/net
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/test/net
Fresh start
Diffstat (limited to 'jni/ruby/test/net')
-rw-r--r--jni/ruby/test/net/ftp/test_buffered_socket.rb40
-rw-r--r--jni/ruby/test/net/ftp/test_ftp.rb833
-rw-r--r--jni/ruby/test/net/http/test_buffered_io.rb17
-rw-r--r--jni/ruby/test/net/http/test_http.rb933
-rw-r--r--jni/ruby/test/net/http/test_http_request.rb79
-rw-r--r--jni/ruby/test/net/http/test_httpheader.rb334
-rw-r--r--jni/ruby/test/net/http/test_httpresponse.rb394
-rw-r--r--jni/ruby/test/net/http/test_httpresponses.rb24
-rw-r--r--jni/ruby/test/net/http/test_https.rb194
-rw-r--r--jni/ruby/test/net/http/test_https_proxy.rb46
-rw-r--r--jni/ruby/test/net/http/utils.rb108
-rw-r--r--jni/ruby/test/net/imap/Makefile15
-rw-r--r--jni/ruby/test/net/imap/cacert.pem66
-rw-r--r--jni/ruby/test/net/imap/server.crt48
-rw-r--r--jni/ruby/test/net/imap/server.key15
-rw-r--r--jni/ruby/test/net/imap/test_imap.rb550
-rw-r--r--jni/ruby/test/net/imap/test_imap_response_parser.rb292
-rw-r--r--jni/ruby/test/net/pop/test_pop.rb136
-rw-r--r--jni/ruby/test/net/protocol/test_protocol.rb28
-rw-r--r--jni/ruby/test/net/smtp/test_response.rb99
-rw-r--r--jni/ruby/test/net/smtp/test_smtp.rb54
-rw-r--r--jni/ruby/test/net/smtp/test_ssl_socket.rb91
22 files changed, 4396 insertions, 0 deletions
diff --git a/jni/ruby/test/net/ftp/test_buffered_socket.rb b/jni/ruby/test/net/ftp/test_buffered_socket.rb
new file mode 100644
index 0000000..f9eefcd
--- /dev/null
+++ b/jni/ruby/test/net/ftp/test_buffered_socket.rb
@@ -0,0 +1,40 @@
+require "net/ftp"
+require "test/unit"
+require "ostruct"
+require "stringio"
+
+class FTPTest < Test::Unit::TestCase
+ def test_gets_empty
+ sock = create_buffered_socket("")
+ assert_equal(nil, sock.gets)
+ end
+
+ def test_gets_one_line
+ sock = create_buffered_socket("foo\n")
+ assert_equal("foo\n", sock.gets)
+ end
+
+ def test_gets_one_line_without_term
+ sock = create_buffered_socket("foo")
+ assert_equal("foo", sock.gets)
+ end
+
+ def test_gets_two_lines
+ sock = create_buffered_socket("foo\nbar\n")
+ assert_equal("foo\n", sock.gets)
+ assert_equal("bar\n", sock.gets)
+ end
+
+ def test_gets_two_lines_without_term
+ sock = create_buffered_socket("foo\nbar")
+ assert_equal("foo\n", sock.gets)
+ assert_equal("bar", sock.gets)
+ end
+
+ private
+
+ def create_buffered_socket(s)
+ io = StringIO.new(s)
+ return Net::FTP::BufferedSocket.new(io)
+ end
+end
diff --git a/jni/ruby/test/net/ftp/test_ftp.rb b/jni/ruby/test/net/ftp/test_ftp.rb
new file mode 100644
index 0000000..eaed3b0
--- /dev/null
+++ b/jni/ruby/test/net/ftp/test_ftp.rb
@@ -0,0 +1,833 @@
+require "net/ftp"
+require "test/unit"
+require "ostruct"
+require "stringio"
+
+class FTPTest < Test::Unit::TestCase
+ SERVER_ADDR = "127.0.0.1"
+
+ def setup
+ @thread = nil
+ end
+
+ def teardown
+ if @thread
+ @thread.join
+ end
+ end
+
+ def test_not_connected
+ ftp = Net::FTP.new
+ assert_raise(Net::FTPConnectionError) do
+ ftp.quit
+ end
+ end
+
+ def test_connect_fail
+ server = create_ftp_server { |sock|
+ sock.print("421 Service not available, closing control connection.\r\n")
+ }
+ begin
+ ftp = Net::FTP.new
+ assert_raise(Net::FTPTempError){ ftp.connect(SERVER_ADDR, server.port) }
+ ensure
+ ftp.close if ftp
+ server.close
+ end
+ end
+
+ def test_parse227
+ ftp = Net::FTP.new
+ host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)")
+ assert_equal("192.168.0.1", host)
+ assert_equal(3106, port)
+ assert_raise(Net::FTPReplyError) do
+ ftp.send(:parse227, "500 Syntax error")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 Entering Passive Mode")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34,56)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 ) foo bar (")
+ end
+ end
+
+ def test_parse228
+ ftp = Net::FTP.new
+ host, port = ftp.send(:parse228, "228 Entering Long Passive Mode (4,4,192,168,0,1,2,12,34)")
+ assert_equal("192.168.0.1", host)
+ assert_equal(3106, port)
+ host, port = ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34)")
+ assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
+ assert_equal(3106, port)
+ assert_raise(Net::FTPReplyError) do
+ ftp.send(:parse228, "500 Syntax error")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Passive Mode")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (6,4,192,168,0,1,2,12,34)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (4,4,192,168,0,1,3,12,34,56)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (4,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,3,12,34,56)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34,56)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 ) foo bar (")
+ end
+ end
+
+ def test_parse229
+ ftp = Net::FTP.new
+ sock = OpenStruct.new
+ sock.peeraddr = [nil, nil, nil, "1080:0000:0000:0000:0008:0800:200c:417a"]
+ ftp.instance_variable_set(:@sock, sock)
+ host, port = ftp.send(:parse229, "229 Entering Passive Mode (|||3106|)")
+ assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
+ assert_equal(3106, port)
+ host, port = ftp.send(:parse229, "229 Entering Passive Mode (!!!3106!)")
+ assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
+ assert_equal(3106, port)
+ host, port = ftp.send(:parse229, "229 Entering Passive Mode (~~~3106~)")
+ assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
+ assert_equal(3106, port)
+ assert_raise(Net::FTPReplyError) do
+ ftp.send(:parse229, "500 Syntax error")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 Entering Passive Mode")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 Entering Passive Mode (|!!3106!)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 Entering Passive Mode ( 3106 )")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 Entering Passive Mode (\x7f\x7f\x7f3106\x7f)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 ) foo bar (")
+ end
+ end
+
+ def test_parse_pasv_port
+ ftp = Net::FTP.new
+ assert_equal(12, ftp.send(:parse_pasv_port, "12"))
+ assert_equal(3106, ftp.send(:parse_pasv_port, "12,34"))
+ assert_equal(795192, ftp.send(:parse_pasv_port, "12,34,56"))
+ assert_equal(203569230, ftp.send(:parse_pasv_port, "12,34,56,78"))
+ end
+
+ def test_login
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_login_fail1
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("502 Command not implemented.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.connect(SERVER_ADDR, server.port)
+ assert_raise(Net::FTPPermError){ ftp.login }
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_login_fail2
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("530 Not logged in.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.connect(SERVER_ADDR, server.port)
+ assert_raise(Net::FTPPermError){ ftp.login }
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ # TODO: How can we test open_timeout? sleep before accept cannot delay
+ # connections.
+ def _test_open_timeout_exceeded
+ commands = []
+ server = create_ftp_server(0.2) { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.open_timeout = 0.1
+ ftp.connect(SERVER_ADDR, server.port)
+ assert_raise(Net::OpenTimeout) do
+ ftp.login
+ end
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_read_timeout_exceeded
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sleep(0.3)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ assert_raise(Net::ReadTimeout) do
+ ftp.login
+ end
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_read_timeout_not_exceeded
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close
+ assert_equal(0.2, ftp.read_timeout)
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_list_read_timeout_exceeded
+ commands = []
+ list_lines = [
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 foo.txt",
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 bar.txt",
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 baz.txt"
+ ]
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Here comes the directory listing.\r\n")
+ begin
+ conn = TCPSocket.new(host, port)
+ list_lines.each_with_index do |l, i|
+ if i == 1
+ sleep(0.5)
+ else
+ sleep(0.1)
+ end
+ conn.print(l, "\r\n")
+ end
+ rescue Errno::EPIPE
+ ensure
+ assert_nil($!)
+ conn.close
+ end
+ sock.print("226 Directory send OK.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::ReadTimeout) do
+ ftp.list
+ end
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("LIST\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_list_read_timeout_not_exceeded
+ commands = []
+ list_lines = [
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 foo.txt",
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 bar.txt",
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 baz.txt"
+ ]
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Here comes the directory listing.\r\n")
+ conn = TCPSocket.new(host, port)
+ list_lines.each do |l|
+ sleep(0.1)
+ conn.print(l, "\r\n")
+ end
+ conn.close
+ sock.print("226 Directory send OK.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(list_lines, ftp.list)
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("LIST\r\n", commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_list_fail
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("553 Requested action not taken.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::FTPPermError){ ftp.list }
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("LIST\r\n", commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_open_data_port_fail_no_leak
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("421 Service not available, closing control connection.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::FTPTempError){ ftp.list }
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_retrbinary_read_timeout_exceeded
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = TCPSocket.new(host, port)
+ sleep(0.1)
+ conn.print(binary_data[0,1024])
+ sleep(0.5)
+ conn.print(binary_data[1024, 1024]) rescue nil # may raise EPIPE or something
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ""
+ assert_raise(Net::ReadTimeout) do
+ ftp.retrbinary("RETR foo", 1024) do |s|
+ buf << s
+ end
+ end
+ assert_equal(1024, buf.bytesize)
+ assert_equal(binary_data[0, 1024], buf)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close unless ftp.closed?
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_retrbinary_read_timeout_not_exceeded
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = TCPSocket.new(host, port)
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ sleep(0.1)
+ conn.print(s)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ""
+ ftp.retrbinary("RETR foo", 1024) do |s|
+ buf << s
+ end
+ assert_equal(binary_data.bytesize, buf.bytesize)
+ assert_equal(binary_data, buf)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_retrbinary_fail
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("550 Requested action not taken.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::FTPPermError){ ftp.retrbinary("RETR foo", 1024) }
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_storbinary
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ stored_data = nil
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo\r\n")
+ conn = TCPSocket.new(host, port)
+ stored_data = conn.read
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024)
+ assert_equal(binary_data, stored_data)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("STOR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_storbinary_fail
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("452 Requested file action aborted.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::FTPTempError){ ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024) }
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("STOR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_abort
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.recv(1024))
+ sock.print("225 No transfer to ABOR.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ #ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.abort
+ assert_equal("ABOR\r", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_status
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.recv(1024))
+ sock.print("211 End of status\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.status
+ assert_equal("STAT\r", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ private
+
+
+ def create_ftp_server(sleep_time = nil)
+ server = TCPServer.new(SERVER_ADDR, 0)
+ @thread = Thread.start do
+ if sleep_time
+ sleep(sleep_time)
+ end
+ sock = server.accept
+ begin
+ yield(sock)
+ sock.shutdown(Socket::SHUT_WR)
+ sock.read unless sock.eof?
+ ensure
+ sock.close
+ end
+ end
+ def server.port
+ addr[1]
+ end
+ return server
+ end
+end
diff --git a/jni/ruby/test/net/http/test_buffered_io.rb b/jni/ruby/test/net/http/test_buffered_io.rb
new file mode 100644
index 0000000..e24e7c1
--- /dev/null
+++ b/jni/ruby/test/net/http/test_buffered_io.rb
@@ -0,0 +1,17 @@
+require 'test/unit'
+require 'net/http'
+require 'stringio'
+
+require_relative 'utils'
+
+module Net
+ class TestBufferedIO < Test::Unit::TestCase
+ def test_eof?
+ s = StringIO.new
+ assert s.eof?
+ bio = BufferedIO.new(s)
+ assert_equal s, bio.io
+ assert_equal s.eof?, bio.eof?
+ end
+ end
+end
diff --git a/jni/ruby/test/net/http/test_http.rb b/jni/ruby/test/net/http/test_http.rb
new file mode 100644
index 0000000..9d5cf39
--- /dev/null
+++ b/jni/ruby/test/net/http/test_http.rb
@@ -0,0 +1,933 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'net/http'
+require 'stringio'
+require_relative 'utils'
+
+class TestNetHTTP < Test::Unit::TestCase
+
+ def test_class_Proxy
+ no_proxy_class = Net::HTTP.Proxy nil
+
+ assert_equal Net::HTTP, no_proxy_class
+
+ proxy_class = Net::HTTP.Proxy 'proxy.example', 8000, 'user', 'pass'
+
+ refute_equal Net::HTTP, proxy_class
+
+ assert_operator proxy_class, :<, Net::HTTP
+
+ assert_equal 'proxy.example', proxy_class.proxy_address
+ assert_equal 8000, proxy_class.proxy_port
+ assert_equal 'user', proxy_class.proxy_user
+ assert_equal 'pass', proxy_class.proxy_pass
+
+ http = proxy_class.new 'example'
+
+ refute http.proxy_from_env?
+
+
+ proxy_class = Net::HTTP.Proxy 'proxy.example'
+ assert_equal 'proxy.example', proxy_class.proxy_address
+ assert_equal 80, proxy_class.proxy_port
+ end
+
+ def test_class_Proxy_from_ENV
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ # These are ignored on purpose. See Bug 4388 and Feature 6546
+ ENV['http_proxy_user'] = 'user'
+ ENV['http_proxy_pass'] = 'pass'
+
+ proxy_class = Net::HTTP.Proxy :ENV
+
+ refute_equal Net::HTTP, proxy_class
+
+ assert_operator proxy_class, :<, Net::HTTP
+
+ assert_nil proxy_class.proxy_address
+ assert_nil proxy_class.proxy_user
+ assert_nil proxy_class.proxy_pass
+
+ refute_equal 8000, proxy_class.proxy_port
+
+ http = proxy_class.new 'example'
+
+ assert http.proxy_from_env?
+ end
+ end
+
+ def test_edit_path
+ http = Net::HTTP.new 'example', nil, nil
+
+ edited = http.send :edit_path, '/path'
+
+ assert_equal '/path', edited
+
+ http.use_ssl = true
+
+ edited = http.send :edit_path, '/path'
+
+ assert_equal '/path', edited
+ end
+
+ def test_edit_path_proxy
+ http = Net::HTTP.new 'example', nil, 'proxy.example'
+
+ edited = http.send :edit_path, '/path'
+
+ assert_equal 'http://example/path', edited
+
+ http.use_ssl = true
+
+ edited = http.send :edit_path, '/path'
+
+ assert_equal '/path', edited
+ end
+
+ def test_proxy_address
+ clean_http_proxy_env do
+ http = Net::HTTP.new 'example', nil, 'proxy.example'
+ assert_equal 'proxy.example', http.proxy_address
+
+ http = Net::HTTP.new 'example', nil
+ assert_equal nil, http.proxy_address
+ end
+ end
+
+ def test_proxy_address_ENV
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ http = Net::HTTP.new 'example'
+
+ assert_equal 'proxy.example', http.proxy_address
+ end
+ end
+
+ def test_proxy_eh_no_proxy
+ clean_http_proxy_env do
+ assert_equal false, Net::HTTP.new('example', nil, nil).proxy?
+ end
+ end
+
+ def test_proxy_eh_ENV
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ http = Net::HTTP.new 'example'
+
+ assert_equal true, http.proxy?
+ end
+ end
+
+ def test_proxy_eh_ENV_none_set
+ clean_http_proxy_env do
+ assert_equal false, Net::HTTP.new('example').proxy?
+ end
+ end
+
+ def test_proxy_eh_ENV_no_proxy
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+ ENV['no_proxy'] = 'example'
+
+ assert_equal false, Net::HTTP.new('example').proxy?
+ end
+ end
+
+ def test_proxy_port
+ clean_http_proxy_env do
+ http = Net::HTTP.new 'exmaple', nil, 'proxy.example'
+ assert_equal 'proxy.example', http.proxy_address
+ assert_equal 80, http.proxy_port
+ http = Net::HTTP.new 'exmaple', nil, 'proxy.example', 8000
+ assert_equal 8000, http.proxy_port
+ http = Net::HTTP.new 'exmaple', nil
+ assert_equal nil, http.proxy_port
+ end
+ end
+
+ def test_proxy_port_ENV
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ http = Net::HTTP.new 'example'
+
+ assert_equal 8000, http.proxy_port
+ end
+ end
+
+ def test_newobj
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ http = Net::HTTP.newobj 'example'
+
+ assert_equal false, http.proxy?
+ end
+ end
+
+ def clean_http_proxy_env
+ orig = {
+ 'http_proxy' => ENV['http_proxy'],
+ 'http_proxy_user' => ENV['http_proxy_user'],
+ 'http_proxy_pass' => ENV['http_proxy_pass'],
+ 'no_proxy' => ENV['no_proxy'],
+ }
+
+ orig.each_key do |key|
+ ENV.delete key
+ end
+
+ yield
+ ensure
+ orig.each do |key, value|
+ ENV[key] = value
+ end
+ end
+
+end
+
+module TestNetHTTP_version_1_1_methods
+
+ def test_s_get
+ assert_equal $test_net_http_data,
+ Net::HTTP.get(config('host'), '/', config('port'))
+ end
+
+ def test_head
+ start {|http|
+ res = http.head('/')
+ assert_kind_of Net::HTTPResponse, res
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_equal $test_net_http_data.size, res['Content-Length'].to_i
+ end
+ }
+ end
+
+ def test_get
+ start {|http|
+ _test_get__get http
+ _test_get__iter http
+ _test_get__chunked http
+ }
+ end
+
+ def _test_get__get(http)
+ res = http.get('/')
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+
+ assert_nothing_raised {
+ http.get('/', { 'User-Agent' => 'test' }.freeze)
+ }
+
+ assert res.decode_content, '[Bug #7924]' if Net::HTTP::HAVE_ZLIB
+ end
+
+ def _test_get__iter(http)
+ buf = ''
+ res = http.get('/') {|s| buf << s }
+ assert_kind_of Net::HTTPResponse, res
+ # assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_equal $test_net_http_data.size, buf.size
+ assert_equal $test_net_http_data, buf
+ # assert_equal $test_net_http_data.size, res.body.size
+ # assert_equal $test_net_http_data, res.body
+ end
+
+ def _test_get__chunked(http)
+ buf = ''
+ res = http.get('/') {|s| buf << s }
+ assert_kind_of Net::HTTPResponse, res
+ # assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_equal $test_net_http_data.size, buf.size
+ assert_equal $test_net_http_data, buf
+ # assert_equal $test_net_http_data.size, res.body.size
+ # assert_equal $test_net_http_data, res.body
+ end
+
+ def test_get__break
+ i = 0
+ start {|http|
+ http.get('/') do |str|
+ i += 1
+ break
+ end
+ }
+ assert_equal 1, i
+ @log_tester = nil # server may encount ECONNRESET
+ end
+
+ def test_get__implicit_start
+ res = new().get('/')
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+ end
+
+ def test_get2
+ start {|http|
+ http.get2('/') {|res|
+ EnvUtil.suppress_warning do
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of Net::HTTPResponse, res.header
+ end
+
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_kind_of String, res.body
+ assert_kind_of String, res.entity
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+ assert_equal $test_net_http_data, res.entity
+ }
+ }
+ end
+
+ def test_post
+ start {|http|
+ _test_post__base http
+ _test_post__file http
+ _test_post__no_data http
+ }
+ end
+
+ def _test_post__base(http)
+ uheader = {}
+ uheader['Accept'] = 'application/octet-stream'
+ uheader['Content-Type'] = 'application/x-www-form-urlencoded'
+ data = 'post data'
+ res = http.post('/', data, uheader)
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ assert_equal data, res.body
+ assert_equal data, res.entity
+ end
+
+ def _test_post__file(http)
+ data = 'post data'
+ f = StringIO.new
+ http.post('/', data, {'content-type' => 'application/x-www-form-urlencoded'}, f)
+ assert_equal data, f.string
+ end
+
+ def _test_post__no_data(http)
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ EnvUtil.suppress_warning do
+ data = nil
+ res = http.post('/', data)
+ assert_not_equal '411', res.code
+ end
+ end
+ end
+
+ def test_s_post_form
+ url = "http://#{config('host')}:#{config('port')}/"
+ res = Net::HTTP.post_form(
+ URI.parse(url),
+ "a" => "x")
+ assert_equal ["a=x"], res.body.split(/[;&]/).sort
+
+ res = Net::HTTP.post_form(
+ URI.parse(url),
+ "a" => "x",
+ "b" => "y")
+ assert_equal ["a=x", "b=y"], res.body.split(/[;&]/).sort
+
+ res = Net::HTTP.post_form(
+ URI.parse(url),
+ "a" => ["x1", "x2"],
+ "b" => "y")
+ assert_equal url, res['X-request-uri']
+ assert_equal ["a=x1", "a=x2", "b=y"], res.body.split(/[;&]/).sort
+
+ res = Net::HTTP.post_form(
+ URI.parse(url + '?a=x'),
+ "b" => "y")
+ assert_equal url + '?a=x', res['X-request-uri']
+ assert_equal ["b=y"], res.body.split(/[;&]/).sort
+ end
+
+ def test_patch
+ start {|http|
+ _test_patch__base http
+ }
+ end
+
+ def _test_patch__base(http)
+ uheader = {}
+ uheader['Accept'] = 'application/octet-stream'
+ uheader['Content-Type'] = 'application/x-www-form-urlencoded'
+ data = 'patch data'
+ res = http.patch('/', data, uheader)
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ assert_equal data, res.body
+ assert_equal data, res.entity
+ end
+
+ def test_timeout_during_HTTP_session
+ bug4246 = "expected the HTTP session to have timed out but have not. c.f. [ruby-core:34203]"
+
+ th = nil
+ # listen for connections... but deliberately do not read
+ TCPServer.open('localhost', 0) {|server|
+ port = server.addr[1]
+
+ conn = Net::HTTP.new('localhost', port)
+ conn.read_timeout = 0.01
+ conn.open_timeout = 0.1
+
+ th = Thread.new do
+ assert_raise(Net::ReadTimeout) {
+ conn.get('/')
+ }
+ end
+ assert th.join(10), bug4246
+ }
+ ensure
+ th.kill
+ th.join
+ end
+end
+
+
+module TestNetHTTP_version_1_2_methods
+
+ def test_request
+ start {|http|
+ _test_request__GET http
+ _test_request__accept_encoding http
+ _test_request__file http
+ # _test_request__range http # WEBrick does not support Range: header.
+ _test_request__HEAD http
+ _test_request__POST http
+ _test_request__stream_body http
+ _test_request__uri http
+ _test_request__uri_host http
+ }
+ end
+
+ def _test_request__GET(http)
+ req = Net::HTTP::Get.new('/')
+ http.request(req) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+
+ assert res.decode_content, 'Bug #7831' if Net::HTTP::HAVE_ZLIB
+ }
+ end
+
+ def _test_request__accept_encoding(http)
+ req = Net::HTTP::Get.new('/', 'accept-encoding' => 'deflate')
+ http.request(req) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+
+ refute res.decode_content, 'Bug #7831' if Net::HTTP::HAVE_ZLIB
+ }
+ end
+
+ def _test_request__file(http)
+ req = Net::HTTP::Get.new('/')
+ http.request(req) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ f = StringIO.new("".force_encoding("ASCII-8BIT"))
+ res.read_body f
+ assert_equal $test_net_http_data.bytesize, f.string.bytesize
+ assert_equal $test_net_http_data.encoding, f.string.encoding
+ assert_equal $test_net_http_data, f.string
+ }
+ end
+
+ def _test_request__range(http)
+ req = Net::HTTP::Get.new('/')
+ req['range'] = 'bytes=0-5'
+ assert_equal $test_net_http_data[0,6], http.request(req).body
+ end
+
+ def _test_request__HEAD(http)
+ req = Net::HTTP::Head.new('/')
+ http.request(req) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_nil res.body
+ }
+ end
+
+ def _test_request__POST(http)
+ data = 'post data'
+ req = Net::HTTP::Post.new('/')
+ req['Accept'] = $test_net_http_data_type
+ req['Content-Type'] = 'application/x-www-form-urlencoded'
+ http.request(req, data) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_equal data.size, res['content-length'].to_i
+ end
+ assert_kind_of String, res.body
+ assert_equal data, res.body
+ }
+ end
+
+ def _test_request__stream_body(http)
+ req = Net::HTTP::Post.new('/')
+ data = $test_net_http_data
+ req.content_length = data.size
+ req['Content-Type'] = 'application/x-www-form-urlencoded'
+ req.body_stream = StringIO.new(data)
+ res = http.request(req)
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ assert_equal data.size, res.body.size
+ assert_equal data, res.body
+ end
+
+ def _test_request__path(http)
+ uri = URI 'https://example/'
+ req = Net::HTTP::Get.new('/')
+
+ res = http.request(req)
+
+ assert_kind_of URI::Generic, req.uri
+
+ refute_equal uri, req.uri
+
+ assert_equal uri, res.uri
+
+ refute_same uri, req.uri
+ refute_same req.uri, res.uri
+ end
+
+ def _test_request__uri(http)
+ uri = URI 'https://example/'
+ req = Net::HTTP::Get.new(uri)
+
+ res = http.request(req)
+
+ assert_kind_of URI::Generic, req.uri
+
+ refute_equal uri, req.uri
+
+ assert_equal req.uri, res.uri
+
+ refute_same uri, req.uri
+ refute_same req.uri, res.uri
+ end
+
+ def _test_request__uri_host(http)
+ uri = URI 'http://other.example/'
+
+ req = Net::HTTP::Get.new(uri)
+ req['host'] = 'example'
+
+ res = http.request(req)
+
+ assert_kind_of URI::Generic, req.uri
+
+ assert_equal URI("http://example:#{http.port}"), res.uri
+ end
+
+ def test_send_request
+ start {|http|
+ _test_send_request__GET http
+ _test_send_request__HEAD http
+ _test_send_request__POST http
+ }
+ end
+
+ def _test_send_request__GET(http)
+ res = http.send_request('GET', '/')
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_kind_of String, res.body
+ assert_equal $test_net_http_data, res.body
+ end
+
+ def _test_send_request__HEAD(http)
+ res = http.send_request('HEAD', '/')
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_nil res.body
+ end
+
+ def _test_send_request__POST(http)
+ data = 'aaabbb cc ddddddddddd lkjoiu4j3qlkuoa'
+ res = http.send_request('POST', '/', data, 'content-type' => 'application/x-www-form-urlencoded')
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ assert_equal data.size, res.body.size
+ assert_equal data, res.body
+ end
+
+ def test_set_form
+ require 'tempfile'
+ Tempfile.create('ruby-test') {|file|
+ file << "\u{30c7}\u{30fc}\u{30bf}"
+ data = [
+ ['name', 'Gonbei Nanashi'],
+ ['name', "\u{540d}\u{7121}\u{3057}\u{306e}\u{6a29}\u{5175}\u{885b}"],
+ ['s"i\o', StringIO.new("\u{3042 3044 4e9c 925b}")],
+ ["file", file, filename: "ruby-test"]
+ ]
+ expected = <<"__EOM__".gsub(/\n/, "\r\n")
+--<boundary>
+Content-Disposition: form-data; name="name"
+
+Gonbei Nanashi
+--<boundary>
+Content-Disposition: form-data; name="name"
+
+\xE5\x90\x8D\xE7\x84\xA1\xE3\x81\x97\xE3\x81\xAE\xE6\xA8\xA9\xE5\x85\xB5\xE8\xA1\x9B
+--<boundary>
+Content-Disposition: form-data; name="s\\"i\\\\o"
+
+\xE3\x81\x82\xE3\x81\x84\xE4\xBA\x9C\xE9\x89\x9B
+--<boundary>
+Content-Disposition: form-data; name="file"; filename="ruby-test"
+Content-Type: application/octet-stream
+
+\xE3\x83\x87\xE3\x83\xBC\xE3\x82\xBF
+--<boundary>--
+__EOM__
+ start {|http|
+ _test_set_form_urlencoded(http, data.reject{|k,v|!v.is_a?(String)})
+ _test_set_form_multipart(http, false, data, expected)
+ _test_set_form_multipart(http, true, data, expected)
+ }
+ }
+ end
+
+ def _test_set_form_urlencoded(http, data)
+ req = Net::HTTP::Post.new('/')
+ req.set_form(data)
+ res = http.request req
+ assert_equal "name=Gonbei+Nanashi&name=%E5%90%8D%E7%84%A1%E3%81%97%E3%81%AE%E6%A8%A9%E5%85%B5%E8%A1%9B", res.body
+ end
+
+ def _test_set_form_multipart(http, chunked_p, data, expected)
+ data.each{|k,v|v.rewind rescue nil}
+ req = Net::HTTP::Post.new('/')
+ req.set_form(data, 'multipart/form-data')
+ req['Transfer-Encoding'] = 'chunked' if chunked_p
+ res = http.request req
+ body = res.body
+ assert_match(/\A--(?<boundary>\S+)/, body)
+ /\A--(?<boundary>\S+)/ =~ body
+ expected = expected.gsub(/<boundary>/, boundary)
+ assert_equal(expected, body)
+ end
+
+ def test_set_form_with_file
+ require 'tempfile'
+ Tempfile.create('ruby-test') {|file|
+ file.binmode
+ file << $test_net_http_data
+ filename = File.basename(file.to_path)
+ data = [['file', file]]
+ expected = <<"__EOM__".gsub(/\n/, "\r\n")
+--<boundary>
+Content-Disposition: form-data; name="file"; filename="<filename>"
+Content-Type: application/octet-stream
+
+<data>
+--<boundary>--
+__EOM__
+ expected.sub!(/<filename>/, filename)
+ expected.sub!(/<data>/, $test_net_http_data)
+ start {|http|
+ data.each{|k,v|v.rewind rescue nil}
+ req = Net::HTTP::Post.new('/')
+ req.set_form(data, 'multipart/form-data')
+ res = http.request req
+ body = res.body
+ header, _ = body.split(/\r\n\r\n/, 2)
+ assert_match(/\A--(?<boundary>\S+)/, body)
+ /\A--(?<boundary>\S+)/ =~ body
+ expected = expected.gsub(/<boundary>/, boundary)
+ assert_match(/^--(?<boundary>\S+)\r\n/, header)
+ assert_match(
+ /^Content-Disposition: form-data; name="file"; filename="#{filename}"\r\n/,
+ header)
+ assert_equal(expected, body)
+
+ data.each{|k,v|v.rewind rescue nil}
+ req['Transfer-Encoding'] = 'chunked'
+ res = http.request req
+ #assert_equal(expected, res.body)
+ }
+ }
+ end
+end
+
+class TestNetHTTP_v1_2 < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ }
+
+ include TestNetHTTPUtils
+ include TestNetHTTP_version_1_1_methods
+ include TestNetHTTP_version_1_2_methods
+
+ def new
+ Net::HTTP.version_1_2
+ super
+ end
+end
+
+class TestNetHTTP_v1_2_chunked < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ 'chunked' => true,
+ }
+
+ include TestNetHTTPUtils
+ include TestNetHTTP_version_1_1_methods
+ include TestNetHTTP_version_1_2_methods
+
+ def new
+ Net::HTTP.version_1_2
+ super
+ end
+
+ def test_chunked_break
+ assert_nothing_raised("[ruby-core:29229]") {
+ start {|http|
+ http.request_get('/') {|res|
+ res.read_body {|chunk|
+ break
+ }
+ }
+ }
+ }
+ end
+end
+
+class TestNetHTTPContinue < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ 'chunked' => true,
+ }
+
+ include TestNetHTTPUtils
+
+ def logfile
+ @debug = StringIO.new('')
+ end
+
+ def mount_proc(&block)
+ @server.mount('/continue', WEBrick::HTTPServlet::ProcHandler.new(block.to_proc))
+ end
+
+ def test_expect_continue
+ mount_proc {|req, res|
+ req.continue
+ res.body = req.query['body']
+ }
+ start {|http|
+ uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
+ http.continue_timeout = 0.2
+ http.request_post('/continue', 'body=BODY', uheader) {|res|
+ assert_equal('BODY', res.read_body)
+ }
+ }
+ assert_match(/Expect: 100-continue/, @debug.string)
+ assert_match(/HTTP\/1.1 100 continue/, @debug.string)
+ end
+
+ def test_expect_continue_timeout
+ mount_proc {|req, res|
+ sleep 0.2
+ req.continue # just ignored because it's '100'
+ res.body = req.query['body']
+ }
+ start {|http|
+ uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
+ http.continue_timeout = 0
+ http.request_post('/continue', 'body=BODY', uheader) {|res|
+ assert_equal('BODY', res.read_body)
+ }
+ }
+ assert_match(/Expect: 100-continue/, @debug.string)
+ assert_match(/HTTP\/1.1 100 continue/, @debug.string)
+ end
+
+ def test_expect_continue_error
+ mount_proc {|req, res|
+ res.status = 501
+ res.body = req.query['body']
+ }
+ start {|http|
+ uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
+ http.continue_timeout = 0
+ http.request_post('/continue', 'body=ERROR', uheader) {|res|
+ assert_equal('ERROR', res.read_body)
+ }
+ }
+ assert_match(/Expect: 100-continue/, @debug.string)
+ assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
+ end
+
+ def test_expect_continue_error_while_waiting
+ mount_proc {|req, res|
+ res.status = 501
+ res.body = req.query['body']
+ }
+ start {|http|
+ uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
+ http.continue_timeout = 0.5
+ http.request_post('/continue', 'body=ERROR', uheader) {|res|
+ assert_equal('ERROR', res.read_body)
+ }
+ }
+ assert_match(/Expect: 100-continue/, @debug.string)
+ assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
+ end
+end
+
+class TestNetHTTPKeepAlive < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ 'RequestTimeout' => 1,
+ }
+
+ include TestNetHTTPUtils
+
+ def test_keep_alive_get_auto_reconnect
+ start {|http|
+ res = http.get('/')
+ http.keep_alive_timeout = 1
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ sleep 1.5
+ assert_nothing_raised {
+ res = http.get('/')
+ }
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ }
+ end
+
+ def test_keep_alive_get_auto_retry
+ start {|http|
+ res = http.get('/')
+ http.keep_alive_timeout = 5
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ sleep 1.5
+ res = http.get('/')
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ }
+ end
+
+ def test_keep_alive_server_close
+ def @server.run(sock)
+ sock.close
+ end
+
+ start {|http|
+ assert_raises(EOFError, Errno::ECONNRESET, IOError) {
+ http.get('/')
+ }
+ }
+ end
+end
+
+class TestNetHTTPLocalBind < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => 'localhost',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ }
+
+ include TestNetHTTPUtils
+
+ def test_bind_to_local_host
+ @server.mount_proc('/show_ip') { |req, res| res.body = req.remote_ip }
+
+ http = Net::HTTP.new(config('host'), config('port'))
+ http.local_host = Addrinfo.tcp(config('host'), config('port')).ip_address
+ assert_not_nil(http.local_host)
+ assert_nil(http.local_port)
+
+ res = http.get('/show_ip')
+ assert_equal(http.local_host, res.body)
+ end
+
+ def test_bind_to_local_port
+ @server.mount_proc('/show_port') { |req, res| res.body = req.peeraddr[1].to_s }
+
+ http = Net::HTTP.new(config('host'), config('port'))
+ http.local_host = Addrinfo.tcp(config('host'), config('port')).ip_address
+ http.local_port = Addrinfo.tcp(config('host'), 0).bind {|s|
+ s.local_address.ip_port.to_s
+ }
+ assert_not_nil(http.local_host)
+ assert_not_nil(http.local_port)
+
+ res = http.get('/show_port')
+ assert_equal(http.local_port, res.body)
+ end
+end
+
diff --git a/jni/ruby/test/net/http/test_http_request.rb b/jni/ruby/test/net/http/test_http_request.rb
new file mode 100644
index 0000000..1dcb847
--- /dev/null
+++ b/jni/ruby/test/net/http/test_http_request.rb
@@ -0,0 +1,79 @@
+require 'net/http'
+require 'test/unit'
+require 'stringio'
+
+class HTTPRequestTest < Test::Unit::TestCase
+
+ def test_initialize_GET
+ req = Net::HTTP::Get.new '/'
+
+ assert_equal 'GET', req.method
+ refute req.request_body_permitted?
+ assert req.response_body_permitted?
+
+ expected = {
+ 'accept' => %w[*/*],
+ 'user-agent' => %w[Ruby],
+ }
+
+ expected['accept-encoding'] = %w[gzip;q=1.0,deflate;q=0.6,identity;q=0.3] if
+ Net::HTTP::HAVE_ZLIB
+
+ assert_equal expected, req.to_hash
+ end
+
+ def test_initialize_GET_range
+ req = Net::HTTP::Get.new '/', 'Range' => 'bytes=0-9'
+
+ assert_equal 'GET', req.method
+ refute req.request_body_permitted?
+ assert req.response_body_permitted?
+
+ expected = {
+ 'accept' => %w[*/*],
+ 'user-agent' => %w[Ruby],
+ 'range' => %w[bytes=0-9],
+ }
+
+ assert_equal expected, req.to_hash
+ end
+
+ def test_initialize_HEAD
+ req = Net::HTTP::Head.new '/'
+
+ assert_equal 'HEAD', req.method
+ refute req.request_body_permitted?
+ refute req.response_body_permitted?
+
+ expected = {
+ 'accept' => %w[*/*],
+ 'user-agent' => %w[Ruby],
+ }
+
+ assert_equal expected, req.to_hash
+ end
+
+ def test_initialize_accept_encoding
+ req1 = Net::HTTP::Get.new '/'
+
+ assert req1.decode_content, 'Bug #7831 - automatically decode content'
+
+ req2 = Net::HTTP::Get.new '/', 'accept-encoding' => 'identity'
+
+ refute req2.decode_content,
+ 'Bug #7381 - do not decode content if the user overrides'
+ end if Net::HTTP::HAVE_ZLIB
+
+ def test_header_set
+ req = Net::HTTP::Get.new '/'
+
+ assert req.decode_content, 'Bug #7831 - automatically decode content'
+
+ req['accept-encoding'] = 'identity'
+
+ refute req.decode_content,
+ 'Bug #7831 - do not decode content if the user overrides'
+ end if Net::HTTP::HAVE_ZLIB
+
+end
+
diff --git a/jni/ruby/test/net/http/test_httpheader.rb b/jni/ruby/test/net/http/test_httpheader.rb
new file mode 100644
index 0000000..0623871
--- /dev/null
+++ b/jni/ruby/test/net/http/test_httpheader.rb
@@ -0,0 +1,334 @@
+require 'net/http'
+require 'test/unit'
+
+class HTTPHeaderTest < Test::Unit::TestCase
+
+ class C
+ include Net::HTTPHeader
+ def initialize
+ initialize_http_header({})
+ end
+ attr_accessor :body
+ end
+
+ def setup
+ @c = C.new
+ end
+
+ def test_size
+ assert_equal 0, @c.size
+ @c['a'] = 'a'
+ assert_equal 1, @c.size
+ @c['b'] = 'b'
+ assert_equal 2, @c.size
+ @c['b'] = 'b'
+ assert_equal 2, @c.size
+ @c['c'] = 'c'
+ assert_equal 3, @c.size
+ end
+
+ def test_ASET
+ @c['My-Header'] = 'test string'
+ @c['my-Header'] = 'test string'
+ @c['My-header'] = 'test string'
+ @c['my-header'] = 'test string'
+ @c['MY-HEADER'] = 'test string'
+ assert_equal 1, @c.size
+
+ @c['AaA'] = 'aaa'
+ @c['aaA'] = 'aaa'
+ @c['AAa'] = 'aaa'
+ assert_equal 2, @c.length
+ end
+
+ def test_AREF
+ @c['My-Header'] = 'test string'
+ assert_equal 'test string', @c['my-header']
+ assert_equal 'test string', @c['MY-header']
+ assert_equal 'test string', @c['my-HEADER']
+
+ @c['Next-Header'] = 'next string'
+ assert_equal 'next string', @c['next-header']
+ end
+
+ def test_add_field
+ @c.add_field 'My-Header', 'a'
+ assert_equal 'a', @c['My-Header']
+ assert_equal ['a'], @c.get_fields('My-Header')
+ @c.add_field 'My-Header', 'b'
+ assert_equal 'a, b', @c['My-Header']
+ assert_equal ['a', 'b'], @c.get_fields('My-Header')
+ @c.add_field 'My-Header', 'c'
+ assert_equal 'a, b, c', @c['My-Header']
+ assert_equal ['a', 'b', 'c'], @c.get_fields('My-Header')
+ @c.add_field 'My-Header', 'd, d'
+ assert_equal 'a, b, c, d, d', @c['My-Header']
+ assert_equal ['a', 'b', 'c', 'd, d'], @c.get_fields('My-Header')
+ end
+
+ def test_get_fields
+ @c['My-Header'] = 'test string'
+ assert_equal ['test string'], @c.get_fields('my-header')
+ assert_equal ['test string'], @c.get_fields('My-header')
+ assert_equal ['test string'], @c.get_fields('my-Header')
+
+ assert_nil @c.get_fields('not-found')
+ assert_nil @c.get_fields('Not-Found')
+
+ @c.get_fields('my-header').push 'junk'
+ assert_equal ['test string'], @c.get_fields('my-header')
+ @c.get_fields('my-header').clear
+ assert_equal ['test string'], @c.get_fields('my-header')
+ end
+
+ def test_delete
+ @c['My-Header'] = 'test'
+ assert_equal 'test', @c['My-Header']
+ assert_nil @c['not-found']
+ @c.delete 'My-Header'
+ assert_nil @c['My-Header']
+ assert_nil @c['not-found']
+ @c.delete 'My-Header'
+ @c.delete 'My-Header'
+ assert_nil @c['My-Header']
+ assert_nil @c['not-found']
+ end
+
+ def test_each
+ @c['My-Header'] = 'test'
+ @c.each do |k, v|
+ assert_equal 'my-header', k
+ assert_equal 'test', v
+ end
+ @c.each do |k, v|
+ assert_equal 'my-header', k
+ assert_equal 'test', v
+ end
+ end
+
+ def test_each_key
+ @c['My-Header'] = 'test'
+ @c.each_key do |k|
+ assert_equal 'my-header', k
+ end
+ @c.each_key do |k|
+ assert_equal 'my-header', k
+ end
+ end
+
+ def test_each_value
+ @c['My-Header'] = 'test'
+ @c.each_value do |v|
+ assert_equal 'test', v
+ end
+ @c.each_value do |v|
+ assert_equal 'test', v
+ end
+ end
+
+ def test_canonical_each
+ @c['my-header'] = ['a', 'b']
+ @c.canonical_each do |k,v|
+ assert_equal 'My-Header', k
+ assert_equal 'a, b', v
+ end
+ end
+
+ def test_each_capitalized
+ @c['my-header'] = ['a', 'b']
+ @c.each_capitalized do |k,v|
+ assert_equal 'My-Header', k
+ assert_equal 'a, b', v
+ end
+ end
+
+ def test_key?
+ @c['My-Header'] = 'test'
+ assert_equal true, @c.key?('My-Header')
+ assert_equal true, @c.key?('my-header')
+ assert_equal false, @c.key?('Not-Found')
+ assert_equal false, @c.key?('not-found')
+ assert_equal false, @c.key?('')
+ assert_equal false, @c.key?('x' * 1024)
+ end
+
+ def test_to_hash
+ end
+
+ def test_range
+ try_range([1..5], '1-5')
+ try_invalid_range('5-1')
+ try_range([234..567], '234-567')
+ try_range([-5..-1], '-5')
+ try_invalid_range('-0')
+ try_range([1..-1], '1-')
+ try_range([0..0,-1..-1], '0-0,-1')
+ try_range([1..2, 3..4], '1-2,3-4')
+ try_range([1..2, 3..4], '1-2 , 3-4')
+ try_range([1..2, 1..4], '1-2,1-4')
+
+ try_invalid_range('invalid')
+ try_invalid_range(' 12-')
+ try_invalid_range('12- ')
+ try_invalid_range('123-abc')
+ try_invalid_range('abc-123')
+ end
+
+ def try_range(r, s)
+ @c['range'] = "bytes=#{s}"
+ assert_equal r, @c.range
+ end
+
+ def try_invalid_range(s)
+ @c['range'] = "bytes=#{s}"
+ assert_raise(Net::HTTPHeaderSyntaxError, s){ @c.range }
+ end
+
+ def test_range=
+ @c.range = 0..499
+ assert_equal 'bytes=0-499', @c['range']
+ @c.range = 0...500
+ assert_equal 'bytes=0-499', @c['range']
+ @c.range = 300
+ assert_equal 'bytes=0-299', @c['range']
+ @c.range = -400
+ assert_equal 'bytes=-400', @c['range']
+ @c.set_range 0, 500
+ assert_equal 'bytes=0-499', @c['range']
+ end
+
+ def test_content_range
+ end
+
+ def test_range_length
+ @c['Content-Range'] = "bytes 0-499/1000"
+ assert_equal 500, @c.range_length
+ @c['Content-Range'] = "bytes 1-500/1000"
+ assert_equal 500, @c.range_length
+ @c['Content-Range'] = "bytes 1-1/1000"
+ assert_equal 1, @c.range_length
+ end
+
+ def test_chunked?
+ try_chunked true, 'chunked'
+ try_chunked true, ' chunked '
+ try_chunked true, '(OK)chunked'
+
+ try_chunked false, 'not-chunked'
+ try_chunked false, 'chunked-but-not-chunked'
+ end
+
+ def try_chunked(bool, str)
+ @c['transfer-encoding'] = str
+ assert_equal bool, @c.chunked?
+ end
+
+ def test_content_length
+ @c.delete('content-length')
+ assert_nil @c['content-length']
+
+ try_content_length 500, '500'
+ try_content_length 10000_0000_0000, '1000000000000'
+ try_content_length 123, ' 123'
+ try_content_length 1, '1 23'
+ try_content_length 500, '(OK)500'
+ assert_raise(Net::HTTPHeaderSyntaxError, 'here is no digit, but') {
+ @c['content-length'] = 'no digit'
+ @c.content_length
+ }
+ end
+
+ def try_content_length(len, str)
+ @c['content-length'] = str
+ assert_equal len, @c.content_length
+ end
+
+ def test_content_length=
+ @c.content_length = 0
+ assert_equal 0, @c.content_length
+ @c.content_length = 1
+ assert_equal 1, @c.content_length
+ @c.content_length = 999
+ assert_equal 999, @c.content_length
+ @c.content_length = 10000000000000
+ assert_equal 10000000000000, @c.content_length
+ end
+
+ def test_content_type
+ assert_nil @c.content_type
+ @c.content_type = 'text/html'
+ assert_equal 'text/html', @c.content_type
+ @c.content_type = 'application/pdf'
+ assert_equal 'application/pdf', @c.content_type
+ @c.set_content_type 'text/html', {'charset' => 'iso-2022-jp'}
+ assert_equal 'text/html', @c.content_type
+ @c.content_type = 'text'
+ assert_equal 'text', @c.content_type
+ end
+
+ def test_main_type
+ assert_nil @c.main_type
+ @c.content_type = 'text/html'
+ assert_equal 'text', @c.main_type
+ @c.content_type = 'application/pdf'
+ assert_equal 'application', @c.main_type
+ @c.set_content_type 'text/html', {'charset' => 'iso-2022-jp'}
+ assert_equal 'text', @c.main_type
+ @c.content_type = 'text'
+ assert_equal 'text', @c.main_type
+ end
+
+ def test_sub_type
+ assert_nil @c.sub_type
+ @c.content_type = 'text/html'
+ assert_equal 'html', @c.sub_type
+ @c.content_type = 'application/pdf'
+ assert_equal 'pdf', @c.sub_type
+ @c.set_content_type 'text/html', {'charset' => 'iso-2022-jp'}
+ assert_equal 'html', @c.sub_type
+ @c.content_type = 'text'
+ assert_nil @c.sub_type
+ end
+
+ def test_type_params
+ assert_equal({}, @c.type_params)
+ @c.content_type = 'text/html'
+ assert_equal({}, @c.type_params)
+ @c.content_type = 'application/pdf'
+ assert_equal({}, @c.type_params)
+ @c.set_content_type 'text/html', {'charset' => 'iso-2022-jp'}
+ assert_equal({'charset' => 'iso-2022-jp'}, @c.type_params)
+ @c.content_type = 'text'
+ assert_equal({}, @c.type_params)
+ end
+
+ def test_set_content_type
+ end
+
+ def test_form_data=
+ @c.form_data = {"cmd"=>"search", "q"=>"ruby", "max"=>"50"}
+ assert_equal 'application/x-www-form-urlencoded', @c.content_type
+ assert_equal %w( cmd=search max=50 q=ruby ), @c.body.split('&').sort
+ end
+
+ def test_set_form_data
+ @c.set_form_data "cmd"=>"search", "q"=>"ruby", "max"=>"50"
+ assert_equal 'application/x-www-form-urlencoded', @c.content_type
+ assert_equal %w( cmd=search max=50 q=ruby ), @c.body.split('&').sort
+
+ @c.set_form_data "cmd"=>"search", "q"=>"ruby", "max"=>50
+ assert_equal 'application/x-www-form-urlencoded', @c.content_type
+ assert_equal %w( cmd=search max=50 q=ruby ), @c.body.split('&').sort
+
+ @c.set_form_data({"cmd"=>"search", "q"=>"ruby", "max"=>"50"}, ';')
+ assert_equal 'application/x-www-form-urlencoded', @c.content_type
+ assert_equal %w( cmd=search max=50 q=ruby ), @c.body.split(';').sort
+ end
+
+ def test_basic_auth
+ end
+
+ def test_proxy_basic_auth
+ end
+
+end
diff --git a/jni/ruby/test/net/http/test_httpresponse.rb b/jni/ruby/test/net/http/test_httpresponse.rb
new file mode 100644
index 0000000..404c7ae
--- /dev/null
+++ b/jni/ruby/test/net/http/test_httpresponse.rb
@@ -0,0 +1,394 @@
+# coding: US-ASCII
+require 'net/http'
+require 'test/unit'
+require 'stringio'
+
+class HTTPResponseTest < Test::Unit::TestCase
+ def test_singleline_header
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('5', res['content-length'])
+ assert_equal('close', res['connection'])
+ end
+
+ def test_multiline_header
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+X-Foo: XXX
+ YYY
+X-Bar:
+ XXX
+\tYYY
+
+hello
+EOS
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('XXX YYY', res['x-foo'])
+ assert_equal('XXX YYY', res['x-bar'])
+ end
+
+ def test_read_body
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Length: 5
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ assert_equal 'hello', body
+ end
+
+ def test_read_body_block
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Length: 5
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+
+ body = ''
+
+ res.reading_body io, true do
+ res.read_body do |chunk|
+ body << chunk
+ end
+ end
+
+ assert_equal 'hello', body
+ end
+
+ def test_read_body_content_encoding_deflate
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+Content-Length: 13
+
+x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal 'hello', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_uppercase
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: DEFLATE
+Content-Length: 13
+
+x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal 'hello', body
+ else
+ assert_equal 'DEFLATE', res['content-encoding']
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_chunked
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+Transfer-Encoding: chunked
+
+6
+x\x9C\xCBH\xCD\xC9
+7
+\xC9\a\x00\x06,\x02\x15
+0
+
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal 'hello', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_disabled
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+Content-Length: 13
+
+x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = false # user set accept-encoding in request
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ assert_equal 'deflate', res['content-encoding'], 'Bug #7831'
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body, 'Bug #7381'
+ end
+
+ def test_read_body_content_encoding_deflate_no_length
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+
+x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal 'hello', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15\r\n", body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_content_range
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Accept-Ranges: bytes
+Connection: close
+Content-Encoding: gzip
+Content-Length: 10
+Content-Range: bytes 0-9/55
+
+\x1F\x8B\b\x00\x00\x00\x00\x00\x00\x03
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ assert_equal "\x1F\x8B\b\x00\x00\x00\x00\x00\x00\x03", body
+ end
+
+ def test_read_body_content_encoding_deflate_empty_body
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+Content-Length: 0
+
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal '', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal '', body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_empty_body_no_length
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal '', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal '', body
+ end
+ end
+
+ def test_read_body_string
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Length: 5
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+
+ body = ''
+
+ res.reading_body io, true do
+ res.read_body body
+ end
+
+ assert_equal 'hello', body
+ end
+
+ def test_uri_equals
+ uri = URI 'http://example'
+
+ response = Net::HTTPResponse.new '1.1', 200, 'OK'
+
+ response.uri = nil
+
+ assert_nil response.uri
+
+ response.uri = uri
+
+ assert_equal uri, response.uri
+ refute_same uri, response.uri
+ end
+
+ def test_ensure_zero_space_does_not_regress
+ io = dummy_io(<<EOS)
+HTTP/1.1 200OK
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+
+ assert_raises Net::HTTPBadResponse do
+ Net::HTTPResponse.read_new(io)
+ end
+ end
+
+ def test_allow_trailing_space_after_status
+ io = dummy_io(<<EOS)
+HTTP/1.1 200\s
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('1.1', res.http_version)
+ assert_equal('200', res.code)
+ assert_equal('', res.message)
+ end
+
+ def test_normal_status_line
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('1.1', res.http_version)
+ assert_equal('200', res.code)
+ assert_equal('OK', res.message)
+ end
+
+ def test_allow_empty_reason_code
+ io = dummy_io(<<EOS)
+HTTP/1.1 200
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('1.1', res.http_version)
+ assert_equal('200', res.code)
+ assert_equal(nil, res.message)
+ end
+
+private
+
+ def dummy_io(str)
+ str = str.gsub(/\n/, "\r\n")
+
+ Net::BufferedIO.new(StringIO.new(str))
+ end
+end
diff --git a/jni/ruby/test/net/http/test_httpresponses.rb b/jni/ruby/test/net/http/test_httpresponses.rb
new file mode 100644
index 0000000..bf7fbee
--- /dev/null
+++ b/jni/ruby/test/net/http/test_httpresponses.rb
@@ -0,0 +1,24 @@
+require 'net/http'
+require 'test/unit'
+
+class HTTPResponsesTest < Test::Unit::TestCase
+ def test_status_code_classes
+ Net::HTTPResponse::CODE_TO_OBJ.each_pair { |code, klass|
+ case code
+ when /\A1\d\d\z/
+ group = Net::HTTPInformation
+ when /\A2\d\d\z/
+ group = Net::HTTPSuccess
+ when /\A3\d\d\z/
+ group = Net::HTTPRedirection
+ when /\A4\d\d\z/
+ group = Net::HTTPClientError
+ when /\A5\d\d\z/
+ group = Net::HTTPServerError
+ else
+ flunk "Unknown HTTP status code: #{code} => #{klass.name}"
+ end
+ assert(klass < group, "#{klass.name} (#{code}) must inherit from #{group.name}")
+ }
+ end
+end
diff --git a/jni/ruby/test/net/http/test_https.rb b/jni/ruby/test/net/http/test_https.rb
new file mode 100644
index 0000000..8177d94
--- /dev/null
+++ b/jni/ruby/test/net/http/test_https.rb
@@ -0,0 +1,194 @@
+require "test/unit"
+begin
+ require 'net/https'
+ require 'stringio'
+ require 'timeout'
+ require File.expand_path("../../openssl/utils", File.dirname(__FILE__))
+ require File.expand_path("utils", File.dirname(__FILE__))
+rescue LoadError
+ # should skip this test
+end
+
+class TestNetHTTPS < Test::Unit::TestCase
+ include TestNetHTTPUtils
+
+ subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+ exts = [
+ ["keyUsage", "keyEncipherment,digitalSignature", true],
+ ]
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ cert = OpenSSL::TestUtils.issue_cert(
+ subject, key, 1, Time.now, Time.now + 3600, exts,
+ nil, nil, OpenSSL::Digest::SHA1.new
+ )
+
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ 'ssl_enable' => true,
+ 'ssl_certificate' => cert,
+ 'ssl_private_key' => key,
+ }
+
+ def test_get
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+ http.request_get("/") {|res|
+ assert_equal($test_net_http_data, res.body)
+ }
+ rescue SystemCallError
+ skip $!
+ end
+
+ def test_post
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+ data = config('ssl_private_key').to_der
+ http.request_post("/", data, {'content-type' => 'application/x-www-form-urlencoded'}) {|res|
+ assert_equal(data, res.body)
+ }
+ rescue SystemCallError
+ skip $!
+ end
+
+ def test_session_reuse
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+
+ http.start
+ http.get("/")
+ http.finish
+
+ http.start
+ http.get("/")
+ http.finish # three times due to possible bug in OpenSSL 0.9.8
+
+ sid = http.instance_variable_get(:@ssl_session).id
+
+ http.start
+ http.get("/")
+
+ socket = http.instance_variable_get(:@socket).io
+
+ assert socket.session_reused?
+
+ assert_equal sid, http.instance_variable_get(:@ssl_session).id
+
+ http.finish
+ rescue SystemCallError
+ skip $!
+ end
+
+ def test_session_reuse_but_expire
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+
+ http.ssl_timeout = -1
+ http.start
+ http.get("/")
+ http.finish
+
+ sid = http.instance_variable_get(:@ssl_session).id
+
+ http.start
+ http.get("/")
+
+ socket = http.instance_variable_get(:@socket).io
+ assert_equal false, socket.session_reused?
+
+ assert_not_equal sid, http.instance_variable_get(:@ssl_session).id
+
+ http.finish
+ rescue SystemCallError
+ skip $!
+ end
+
+ if ENV["RUBY_OPENSSL_TEST_ALL"]
+ def test_verify
+ http = Net::HTTP.new("ssl.netlab.jp", 443)
+ http.use_ssl = true
+ assert(
+ (http.request_head("/"){|res| } rescue false),
+ "The system may not have default CA certificate store."
+ )
+ end
+ end
+
+ def test_verify_none
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ http.request_get("/") {|res|
+ assert_equal($test_net_http_data, res.body)
+ }
+ rescue SystemCallError
+ skip $!
+ end
+
+ def test_certificate_verify_failure
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ ex = assert_raise(OpenSSL::SSL::SSLError){
+ begin
+ http.request_get("/") {|res| }
+ rescue SystemCallError
+ skip $!
+ end
+ }
+ assert_match(/certificate verify failed/, ex.message)
+ unless /mswin|mingw/ =~ RUBY_PLATFORM
+ # on Windows, Errno::ECONNRESET will be raised, and it'll be eaten by
+ # WEBrick
+ @log_tester = lambda {|log|
+ assert_equal(1, log.length)
+ assert_match(/ERROR OpenSSL::SSL::SSLError:/, log[0])
+ }
+ end
+ end
+
+ def test_identity_verify_failure
+ http = Net::HTTP.new("127.0.0.1", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+ ex = assert_raise(OpenSSL::SSL::SSLError){
+ http.request_get("/") {|res| }
+ }
+ assert_match(/hostname \"127.0.0.1\" does not match/, ex.message)
+ end
+
+ def test_timeout_during_SSL_handshake
+ bug4246 = "expected the SSL connection to have timed out but have not. [ruby-core:34203]"
+
+ # listen for connections... but deliberately do not complete SSL handshake
+ TCPServer.open('localhost', 0) {|server|
+ port = server.addr[1]
+
+ conn = Net::HTTP.new('localhost', port)
+ conn.use_ssl = true
+ conn.read_timeout = 0.01
+ conn.open_timeout = 0.01
+
+ th = Thread.new do
+ assert_raise(Net::OpenTimeout) {
+ conn.get('/')
+ }
+ end
+ assert th.join(10), bug4246
+ }
+ end
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/net/http/test_https_proxy.rb b/jni/ruby/test/net/http/test_https_proxy.rb
new file mode 100644
index 0000000..1c8503b
--- /dev/null
+++ b/jni/ruby/test/net/http/test_https_proxy.rb
@@ -0,0 +1,46 @@
+begin
+ require 'net/https'
+rescue LoadError
+end
+require 'test/unit'
+
+class HTTPSProxyTest < Test::Unit::TestCase
+ def test_https_proxy_authentication
+ begin
+ OpenSSL
+ rescue LoadError
+ skip 'autoload problem. see [ruby-dev:45021][Bug #5786]'
+ end
+
+ TCPServer.open("127.0.0.1", 0) {|serv|
+ _, port, _, _ = serv.addr
+ client_thread = Thread.new {
+ proxy = Net::HTTP.Proxy("127.0.0.1", port, 'user', 'password')
+ http = proxy.new("foo.example.org", 8000)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ begin
+ http.start
+ rescue EOFError
+ end
+ }
+ server_thread = Thread.new {
+ sock = serv.accept
+ begin
+ proxy_request = sock.gets("\r\n\r\n")
+ assert_equal(
+ "CONNECT foo.example.org:8000 HTTP/1.1\r\n" +
+ "Host: foo.example.org:8000\r\n" +
+ "Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n" +
+ "\r\n",
+ proxy_request,
+ "[ruby-dev:25673]")
+ ensure
+ sock.close
+ end
+ }
+ assert_join_threads([client_thread, server_thread])
+ }
+ end
+end if defined?(OpenSSL)
+
diff --git a/jni/ruby/test/net/http/utils.rb b/jni/ruby/test/net/http/utils.rb
new file mode 100644
index 0000000..dcd9469
--- /dev/null
+++ b/jni/ruby/test/net/http/utils.rb
@@ -0,0 +1,108 @@
+require 'webrick'
+begin
+ require "webrick/https"
+rescue LoadError
+ # SSL features cannot be tested
+end
+require 'webrick/httpservlet/abstract'
+
+module TestNetHTTPUtils
+ def start(&block)
+ new().start(&block)
+ end
+
+ def new
+ klass = Net::HTTP::Proxy(config('proxy_host'), config('proxy_port'))
+ http = klass.new(config('host'), config('port'))
+ http.set_debug_output logfile()
+ http
+ end
+
+ def config(key)
+ @config ||= self.class::CONFIG
+ @config[key]
+ end
+
+ def logfile
+ $DEBUG ? $stderr : NullWriter.new
+ end
+
+ def setup
+ spawn_server
+ end
+
+ def teardown
+ if @server
+ @server.shutdown
+ @server_thread.join
+ end
+ @log_tester.call(@log) if @log_tester
+ # resume global state
+ Net::HTTP.version_1_2
+ end
+
+ def spawn_server
+ @log = []
+ @log_tester = lambda {|log| assert_equal([], log ) }
+ @config = self.class::CONFIG
+ server_config = {
+ :BindAddress => config('host'),
+ :Port => 0,
+ :Logger => WEBrick::Log.new(@log, WEBrick::BasicLog::WARN),
+ :AccessLog => [],
+ :ServerType => Thread,
+ }
+ server_config[:OutputBufferSize] = 4 if config('chunked')
+ server_config[:RequestTimeout] = config('RequestTimeout') if config('RequestTimeout')
+ if defined?(OpenSSL) and config('ssl_enable')
+ server_config.update({
+ :SSLEnable => true,
+ :SSLCertificate => config('ssl_certificate'),
+ :SSLPrivateKey => config('ssl_private_key'),
+ :SSLTmpDhCallback => proc { OpenSSL::TestUtils::TEST_KEY_DH1024 },
+ })
+ end
+ @server = WEBrick::HTTPServer.new(server_config)
+ @server.mount('/', Servlet, config('chunked'))
+ @server_thread = @server.start
+ @config['port'] = @server[:Port]
+ end
+
+ $test_net_http = nil
+ $test_net_http_data = (0...256).to_a.map {|i| i.chr }.join('') * 64
+ $test_net_http_data.force_encoding("ASCII-8BIT")
+ $test_net_http_data_type = 'application/octet-stream'
+
+ class Servlet < WEBrick::HTTPServlet::AbstractServlet
+ def initialize(this, chunked = false)
+ @chunked = chunked
+ end
+
+ def do_GET(req, res)
+ res['Content-Type'] = $test_net_http_data_type
+ res.body = $test_net_http_data
+ res.chunked = @chunked
+ end
+
+ # echo server
+ def do_POST(req, res)
+ res['Content-Type'] = req['Content-Type']
+ res['X-request-uri'] = req.request_uri.to_s
+ res.body = req.body
+ res.chunked = @chunked
+ end
+
+ def do_PATCH(req, res)
+ res['Content-Type'] = req['Content-Type']
+ res.body = req.body
+ res.chunked = @chunked
+ end
+ end
+
+ class NullWriter
+ def <<(s) end
+ def puts(*args) end
+ def print(*args) end
+ def printf(*args) end
+ end
+end
diff --git a/jni/ruby/test/net/imap/Makefile b/jni/ruby/test/net/imap/Makefile
new file mode 100644
index 0000000..b2bc9c7
--- /dev/null
+++ b/jni/ruby/test/net/imap/Makefile
@@ -0,0 +1,15 @@
+all:
+
+regen_certs:
+ touch server.key
+ make server.crt
+
+cacert.pem: server.key
+ openssl req -new -x509 -days 1825 -key server.key -out cacert.pem -text -subj "/C=JP/ST=Shimane/L=Matz-e city/O=Ruby Core Team/CN=Ruby Test CA/emailAddress=security@ruby-lang.org"
+
+server.csr:
+ openssl req -new -key server.key -out server.csr -text -subj "/C=JP/ST=Shimane/O=Ruby Core Team/OU=Ruby Test/CN=localhost"
+
+server.crt: server.csr cacert.pem
+ openssl x509 -days 1825 -CA cacert.pem -CAkey server.key -set_serial 00 -in server.csr -req -text -out server.crt
+ rm server.csr
diff --git a/jni/ruby/test/net/imap/cacert.pem b/jni/ruby/test/net/imap/cacert.pem
new file mode 100644
index 0000000..7073387
--- /dev/null
+++ b/jni/ruby/test/net/imap/cacert.pem
@@ -0,0 +1,66 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ b9:90:a2:bf:62:69:17:9c
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
+ Validity
+ Not Before: Jan 3 01:34:17 2014 GMT
+ Not After : Jan 2 01:34:17 2019 GMT
+ Subject: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:db:75:d0:45:de:b1:df:bf:71:a0:0e:b0:a5:e6:
+ bc:f4:1c:9d:e5:25:67:64:c5:7b:cb:f1:af:c6:be:
+ 9a:aa:ea:7e:0f:cc:05:af:ef:40:69:06:b2:c9:13:
+ 9d:7e:eb:a2:06:e2:ea:7d:07:c7:c7:99:c7:fb:d5:
+ b8:eb:63:77:62:2b:18:12:c3:53:58:d0:f5:c7:40:
+ 0c:01:d1:26:82:34:16:09:e3:dc:65:f4:dc:bb:5d:
+ a5:41:60:e7:a9:74:ba:d7:4c:b6:a3:9c:c5:8c:89:
+ af:cb:e8:9f:05:fe:ea:fe:64:24:bf:e7:ed:e3:f6:
+ d0:fc:d6:eb:fc:06:82:10:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ E8:7E:58:AC:13:7B:03:22:8D:9E:AF:32:0B:84:89:80:80:0C:1E:C2
+ X509v3 Authority Key Identifier:
+ keyid:E8:7E:58:AC:13:7B:03:22:8D:9E:AF:32:0B:84:89:80:80:0C:1E:C2
+ DirName:/C=JP/ST=Shimane/L=Matz-e city/O=Ruby Core Team/CN=Ruby Test CA/emailAddress=security@ruby-lang.org
+ serial:B9:90:A2:BF:62:69:17:9C
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 8f:77:06:4e:31:72:12:ee:68:09:70:27:d4:31:85:ef:10:95:
+ f9:0f:2b:66:63:08:37:88:6e:b7:9b:40:3e:18:77:33:86:e8:
+ 61:6a:b7:3c:cb:c7:a6:d6:d5:92:6a:1f:56:d0:9f:5c:32:56:
+ d3:37:52:fe:0e:20:c2:7a:0d:fe:2d:3c:81:da:b8:7f:4d:6a:
+ 08:01:d9:be:7a:a2:15:be:a6:ce:49:64:90:8c:9a:ca:6e:2e:
+ 84:48:1d:94:19:56:94:46:aa:25:9b:68:c2:80:60:bf:cb:2e:
+ 35:03:ea:0a:65:5a:33:38:c6:cc:81:46:c0:bc:36:86:96:39:
+ 10:7d
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAvagAwIBAgIJALmQor9iaRecMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD
+VQQGEwJKUDEQMA4GA1UECBMHU2hpbWFuZTEUMBIGA1UEBxMLTWF0ei1lIGNpdHkx
+FzAVBgNVBAoTDlJ1YnkgQ29yZSBUZWFtMRUwEwYDVQQDEwxSdWJ5IFRlc3QgQ0Ex
+JTAjBgkqhkiG9w0BCQEWFnNlY3VyaXR5QHJ1YnktbGFuZy5vcmcwHhcNMTQwMTAz
+MDEzNDE3WhcNMTkwMTAyMDEzNDE3WjCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgT
+B1NoaW1hbmUxFDASBgNVBAcTC01hdHotZSBjaXR5MRcwFQYDVQQKEw5SdWJ5IENv
+cmUgVGVhbTEVMBMGA1UEAxMMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz
+ZWN1cml0eUBydWJ5LWxhbmcub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDbddBF3rHfv3GgDrCl5rz0HJ3lJWdkxXvL8a/Gvpqq6n4PzAWv70BpBrLJE51+
+66IG4up9B8fHmcf71bjrY3diKxgSw1NY0PXHQAwB0SaCNBYJ49xl9Ny7XaVBYOep
+dLrXTLajnMWMia/L6J8F/ur+ZCS/5+3j9tD81uv8BoIQ+wIDAQABo4H0MIHxMB0G
+A1UdDgQWBBToflisE3sDIo2erzILhImAgAwewjCBwQYDVR0jBIG5MIG2gBToflis
+E3sDIo2erzILhImAgAwewqGBkqSBjzCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgT
+B1NoaW1hbmUxFDASBgNVBAcTC01hdHotZSBjaXR5MRcwFQYDVQQKEw5SdWJ5IENv
+cmUgVGVhbTEVMBMGA1UEAxMMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz
+ZWN1cml0eUBydWJ5LWxhbmcub3JnggkAuZCiv2JpF5wwDAYDVR0TBAUwAwEB/zAN
+BgkqhkiG9w0BAQUFAAOBgQCPdwZOMXIS7mgJcCfUMYXvEJX5DytmYwg3iG63m0A+
+GHczhuhharc8y8em1tWSah9W0J9cMlbTN1L+DiDCeg3+LTyB2rh/TWoIAdm+eqIV
+vqbOSWSQjJrKbi6ESB2UGVaURqolm2jCgGC/yy41A+oKZVozOMbMgUbAvDaGljkQ
+fQ==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/net/imap/server.crt b/jni/ruby/test/net/imap/server.crt
new file mode 100644
index 0000000..fa4f994
--- /dev/null
+++ b/jni/ruby/test/net/imap/server.crt
@@ -0,0 +1,48 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 0 (0x0)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
+ Validity
+ Not Before: Jan 3 01:34:17 2014 GMT
+ Not After : Jan 2 01:34:17 2019 GMT
+ Subject: C=JP, ST=Shimane, O=Ruby Core Team, OU=Ruby Test, CN=localhost
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:db:75:d0:45:de:b1:df:bf:71:a0:0e:b0:a5:e6:
+ bc:f4:1c:9d:e5:25:67:64:c5:7b:cb:f1:af:c6:be:
+ 9a:aa:ea:7e:0f:cc:05:af:ef:40:69:06:b2:c9:13:
+ 9d:7e:eb:a2:06:e2:ea:7d:07:c7:c7:99:c7:fb:d5:
+ b8:eb:63:77:62:2b:18:12:c3:53:58:d0:f5:c7:40:
+ 0c:01:d1:26:82:34:16:09:e3:dc:65:f4:dc:bb:5d:
+ a5:41:60:e7:a9:74:ba:d7:4c:b6:a3:9c:c5:8c:89:
+ af:cb:e8:9f:05:fe:ea:fe:64:24:bf:e7:ed:e3:f6:
+ d0:fc:d6:eb:fc:06:82:10:fb
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 85:f5:d3:05:8b:8c:f4:43:1c:88:f2:8f:b2:f2:93:77:b7:3d:
+ 95:c6:a0:34:bc:33:6a:d8:85:5f:3e:86:08:10:c5:5c:c1:76:
+ a3:53:3c:dc:38:98:23:97:e7:da:21:ac:e8:4d:3c:96:70:29:
+ ff:ff:1e:4a:9a:17:2b:db:04:62:b9:ef:ab:ea:a7:a5:e8:7c:
+ b1:d5:ed:30:a8:6c:78:de:51:7e:e3:8a:c2:a4:64:a8:63:a2:
+ bc:fd:43:9c:f3:55:7d:54:c9:6a:d8:53:1c:4b:6b:03:aa:b6:
+ 19:e6:a4:4f:47:00:96:c5:42:59:85:4e:c3:4e:cd:41:82:53:
+ 10:f8
+-----BEGIN CERTIFICATE-----
+MIICXDCCAcUCAQAwDQYJKoZIhvcNAQEFBQAwgYwxCzAJBgNVBAYTAkpQMRAwDgYD
+VQQIEwdTaGltYW5lMRQwEgYDVQQHEwtNYXR6LWUgY2l0eTEXMBUGA1UEChMOUnVi
+eSBDb3JlIFRlYW0xFTATBgNVBAMTDFJ1YnkgVGVzdCBDQTElMCMGCSqGSIb3DQEJ
+ARYWc2VjdXJpdHlAcnVieS1sYW5nLm9yZzAeFw0xNDAxMDMwMTM0MTdaFw0xOTAx
+MDIwMTM0MTdaMGAxCzAJBgNVBAYTAkpQMRAwDgYDVQQIEwdTaGltYW5lMRcwFQYD
+VQQKEw5SdWJ5IENvcmUgVGVhbTESMBAGA1UECxMJUnVieSBUZXN0MRIwEAYDVQQD
+Ewlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANt10EXesd+/
+caAOsKXmvPQcneUlZ2TFe8vxr8a+mqrqfg/MBa/vQGkGsskTnX7rogbi6n0Hx8eZ
+x/vVuOtjd2IrGBLDU1jQ9cdADAHRJoI0Fgnj3GX03LtdpUFg56l0utdMtqOcxYyJ
+r8vonwX+6v5kJL/n7eP20PzW6/wGghD7AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEA
+hfXTBYuM9EMciPKPsvKTd7c9lcagNLwzatiFXz6GCBDFXMF2o1M83DiYI5fn2iGs
+6E08lnAp//8eSpoXK9sEYrnvq+qnpeh8sdXtMKhseN5RfuOKwqRkqGOivP1DnPNV
+fVTJathTHEtrA6q2GeakT0cAlsVCWYVOw07NQYJTEPg=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/net/imap/server.key b/jni/ruby/test/net/imap/server.key
new file mode 100644
index 0000000..7c57546
--- /dev/null
+++ b/jni/ruby/test/net/imap/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDbddBF3rHfv3GgDrCl5rz0HJ3lJWdkxXvL8a/Gvpqq6n4PzAWv
+70BpBrLJE51+66IG4up9B8fHmcf71bjrY3diKxgSw1NY0PXHQAwB0SaCNBYJ49xl
+9Ny7XaVBYOepdLrXTLajnMWMia/L6J8F/ur+ZCS/5+3j9tD81uv8BoIQ+wIDAQAB
+AoGAGtYHR+P5gFDaxiXFuCPFC1zMeg7e29XCU6gURIteQnQ2QhxCvcbV64HkLu51
+HeYWhB0Pa4aeCWxmpgb2e+JH4MEoIjeJSGyZQeqwkQLgWJDdvkgWx5am58QzA60I
+ipkZ9QHcPffSs5RiGx4yfr58KqAmwFphGCY8W7v4LqaENdECQQD9H5VTW9g4gj1c
+j3uNYvSI/D7a9P7gfI+ziczuwMm5xsBx3D/t5TAr3SJKNne3sl1E6ZERCUbzxf+C
+k58EiHx1AkEA3fRLGqDOq7EcQhbjTcA/v/t5MwlGEUsS9+XrqOWn50YuoIwRZJ3v
+qHRQzfQfFNklGtfBvwQ4md3irXjMeGVprwJBAMEAuwiDiHuV+xm/ofKtmE13IKot
+ksYy1BOOp/8IawhHXueyi+BmF/PqOkIiA+jCjNGF0oIN89beizPSQbbgJx0CQG/K
+qL1bu1ys0y/SeWBi8XkP/0aeaCUzq/UiYCTsrzoEll2UzvnftqMhGsXxLGqCyHaR
+r2s3hA6zvIVlL4+AfM8CQQClq+WDrC5VKciLYakZNWJjV1m+H2Ut/0fXdUjKHajE
+FWLcsrOhADf6bkTb71GwPxnKRkkRmud5upP0ZYYTqM4X
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/net/imap/test_imap.rb b/jni/ruby/test/net/imap/test_imap.rb
new file mode 100644
index 0000000..da33533
--- /dev/null
+++ b/jni/ruby/test/net/imap/test_imap.rb
@@ -0,0 +1,550 @@
+require "net/imap"
+require "test/unit"
+
+class IMAPTest < Test::Unit::TestCase
+ CA_FILE = File.expand_path("cacert.pem", File.dirname(__FILE__))
+ SERVER_KEY = File.expand_path("server.key", File.dirname(__FILE__))
+ SERVER_CERT = File.expand_path("server.crt", File.dirname(__FILE__))
+
+ SERVER_ADDR = "127.0.0.1"
+
+ def setup
+ @do_not_reverse_lookup = Socket.do_not_reverse_lookup
+ Socket.do_not_reverse_lookup = true
+ @threads = []
+ end
+
+ def teardown
+ if !@threads.empty?
+ assert_join_threads(@threads)
+ end
+ ensure
+ Socket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ def test_encode_utf7
+ assert_equal("foo", Net::IMAP.encode_utf7("foo"))
+ assert_equal("&-", Net::IMAP.encode_utf7("&"))
+
+ utf8 = "\357\274\241\357\274\242\357\274\243".force_encoding("UTF-8")
+ s = Net::IMAP.encode_utf7(utf8)
+ assert_equal("&,yH,Iv8j-", s)
+ s = Net::IMAP.encode_utf7("foo&#{utf8}-bar".encode("EUC-JP"))
+ assert_equal("foo&-&,yH,Iv8j--bar", s)
+
+ utf8 = "\343\201\202&".force_encoding("UTF-8")
+ s = Net::IMAP.encode_utf7(utf8)
+ assert_equal("&MEI-&-", s)
+ s = Net::IMAP.encode_utf7(utf8.encode("EUC-JP"))
+ assert_equal("&MEI-&-", s)
+ end
+
+ def test_decode_utf7
+ assert_equal("&", Net::IMAP.decode_utf7("&-"))
+ assert_equal("&-", Net::IMAP.decode_utf7("&--"))
+
+ s = Net::IMAP.decode_utf7("&,yH,Iv8j-")
+ utf8 = "\357\274\241\357\274\242\357\274\243".force_encoding("UTF-8")
+ assert_equal(utf8, s)
+ end
+
+ def test_format_date
+ time = Time.mktime(2009, 7, 24)
+ s = Net::IMAP.format_date(time)
+ assert_equal("24-Jul-2009", s)
+ end
+
+ def test_format_datetime
+ time = Time.mktime(2009, 7, 24, 1, 23, 45)
+ s = Net::IMAP.format_datetime(time)
+ assert_match(/\A24-Jul-2009 01:23 [+\-]\d{4}\z/, s)
+ end
+
+ if defined?(OpenSSL::SSL::SSLError)
+ def test_imaps_unknown_ca
+ assert_raise(OpenSSL::SSL::SSLError) do
+ imaps_test do |port|
+ begin
+ Net::IMAP.new("localhost",
+ :port => port,
+ :ssl => true)
+ rescue SystemCallError
+ skip $!
+ end
+ end
+ end
+ end
+
+ def test_imaps_with_ca_file
+ assert_nothing_raised do
+ imaps_test do |port|
+ begin
+ Net::IMAP.new("localhost",
+ :port => port,
+ :ssl => { :ca_file => CA_FILE })
+ rescue SystemCallError
+ skip $!
+ end
+ end
+ end
+ end
+
+ def test_imaps_verify_none
+ assert_nothing_raised do
+ imaps_test do |port|
+ Net::IMAP.new(SERVER_ADDR,
+ :port => port,
+ :ssl => { :verify_mode => OpenSSL::SSL::VERIFY_NONE })
+ end
+ end
+ end
+
+ def test_imaps_post_connection_check
+ assert_raise(OpenSSL::SSL::SSLError) do
+ imaps_test do |port|
+ # SERVER_ADDR is different from the hostname in the certificate,
+ # so the following code should raise a SSLError.
+ Net::IMAP.new(SERVER_ADDR,
+ :port => port,
+ :ssl => { :ca_file => CA_FILE })
+ end
+ end
+ end
+ end
+
+ if defined?(OpenSSL::SSL)
+ def test_starttls
+ imap = nil
+ starttls_test do |port|
+ imap = Net::IMAP.new("localhost", :port => port)
+ imap.starttls(:ca_file => CA_FILE)
+ imap
+ end
+ rescue SystemCallError
+ skip $!
+ ensure
+ if imap && !imap.disconnected?
+ imap.disconnect
+ end
+ end
+ end
+
+ def test_unexpected_eof
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+# sock.print("* BYE terminating connection\r\n")
+# sock.print("RUBY0001 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ assert_raise(EOFError) do
+ imap.logout
+ end
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
+ def test_idle
+ server = create_tcp_server
+ port = server.addr[1]
+ requests = []
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ requests.push(sock.gets)
+ sock.print("+ idling\r\n")
+ sock.print("* 3 EXISTS\r\n")
+ sock.print("* 2 EXPUNGE\r\n")
+ requests.push(sock.gets)
+ sock.print("RUBY0001 OK IDLE terminated\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)
+ responses = []
+ imap.idle do |res|
+ responses.push(res)
+ if res.name == "EXPUNGE"
+ imap.idle_done
+ end
+ end
+ assert_equal(3, responses.length)
+ assert_instance_of(Net::IMAP::ContinuationRequest, responses[0])
+ assert_equal("EXISTS", responses[1].name)
+ assert_equal(3, responses[1].data)
+ assert_equal("EXPUNGE", responses[2].name)
+ assert_equal(2, responses[2].data)
+ assert_equal(2, requests.length)
+ assert_equal("RUBY0001 IDLE\r\n", requests[0])
+ assert_equal("DONE\r\n", requests[1])
+ imap.logout
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
+ def test_exception_during_idle
+ server = create_tcp_server
+ port = server.addr[1]
+ requests = []
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ requests.push(sock.gets)
+ sock.print("+ idling\r\n")
+ sock.print("* 3 EXISTS\r\n")
+ sock.print("* 2 EXPUNGE\r\n")
+ requests.push(sock.gets)
+ sock.print("RUBY0001 OK IDLE terminated\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)
+ begin
+ th = Thread.current
+ m = Monitor.new
+ in_idle = false
+ exception_raised = false
+ c = m.new_cond
+ @threads << Thread.start do
+ m.synchronize do
+ until in_idle
+ c.wait(0.1)
+ end
+ end
+ th.raise(Interrupt)
+ exception_raised = true
+ end
+ imap.idle do |res|
+ m.synchronize do
+ in_idle = true
+ c.signal
+ until exception_raised
+ c.wait(0.1)
+ end
+ end
+ end
+ rescue Interrupt
+ end
+ assert_equal(2, requests.length)
+ assert_equal("RUBY0001 IDLE\r\n", requests[0])
+ assert_equal("DONE\r\n", requests[1])
+ imap.logout
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
+ def test_idle_done_not_during_idle
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ assert_raise(Net::IMAP::Error) do
+ imap.idle_done
+ end
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
+ def test_unexpected_bye
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK Gimap ready for requests from 75.101.246.151 33if2752585qyk.26\r\n")
+ sock.gets
+ sock.print("* BYE System Error 33if2752585qyk.26\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ assert_raise(Net::IMAP::ByeResponseError) do
+ imap.login("user", "password")
+ end
+ end
+ end
+
+ def test_exception_during_shutdown
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0001 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap.instance_eval do
+ def @sock.shutdown(*args)
+ super
+ ensure
+ raise "error"
+ end
+ end
+ imap.logout
+ ensure
+ assert_raise(RuntimeError) do
+ imap.disconnect
+ end
+ end
+ end
+
+ def test_connection_closed_during_idle
+ server = create_tcp_server
+ port = server.addr[1]
+ requests = []
+ sock = nil
+ threads = []
+ threads << Thread.start do
+ begin
+ sock = server.accept
+ sock.print("* OK test server\r\n")
+ requests.push(sock.gets)
+ sock.print("+ idling\r\n")
+ rescue IOError # sock is closed by another thread
+ ensure
+ server.close
+ end
+ end
+ threads << Thread.start do
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ begin
+ m = Monitor.new
+ in_idle = false
+ exception_raised = false
+ c = m.new_cond
+ threads << Thread.start do
+ m.synchronize do
+ until in_idle
+ c.wait(0.1)
+ end
+ end
+ sock.close
+ exception_raised = true
+ end
+ assert_raise(Net::IMAP::Error) do
+ imap.idle do |res|
+ m.synchronize do
+ in_idle = true
+ c.signal
+ until exception_raised
+ c.wait(0.1)
+ end
+ end
+ end
+ end
+ assert_equal(1, requests.length)
+ assert_equal("RUBY0001 IDLE\r\n", requests[0])
+ ensure
+ imap.disconnect if imap
+ end
+ end
+ assert_join_threads(threads)
+ ensure
+ if sock && !sock.closed?
+ sock.close
+ end
+ end
+
+ def test_connection_closed_without_greeting
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ begin
+ sock = server.accept
+ sock.close
+ ensure
+ server.close
+ end
+ end
+ assert_raise(Net::IMAP::Error) do
+ Net::IMAP.new(SERVER_ADDR, :port => port)
+ end
+ end
+
+ def test_default_port
+ assert_equal(143, Net::IMAP.default_port)
+ assert_equal(143, Net::IMAP.default_imap_port)
+ assert_equal(993, Net::IMAP.default_tls_port)
+ assert_equal(993, Net::IMAP.default_ssl_port)
+ assert_equal(993, Net::IMAP.default_imaps_port)
+ end
+
+ def test_send_invalid_number
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("RUBY0001 OK TEST completed\r\n")
+ sock.gets
+ sock.print("RUBY0002 OK TEST completed\r\n")
+ sock.gets
+ sock.print("RUBY0003 OK TEST completed\r\n")
+ sock.gets
+ sock.print("RUBY0004 OK TEST completed\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0005 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::DataFormatError) do
+ imap.send(:send_command, "TEST", -1)
+ end
+ imap.send(:send_command, "TEST", 0)
+ imap.send(:send_command, "TEST", 4294967295)
+ assert_raise(Net::IMAP::DataFormatError) do
+ imap.send(:send_command, "TEST", 4294967296)
+ end
+ assert_raise(Net::IMAP::DataFormatError) do
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(-1))
+ end
+ assert_raise(Net::IMAP::DataFormatError) do
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(0))
+ end
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(1))
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(4294967295))
+ assert_raise(Net::IMAP::DataFormatError) do
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(4294967296))
+ end
+ imap.logout
+ ensure
+ imap.disconnect
+ end
+ end
+
+ private
+
+ def imaps_test
+ server = create_tcp_server
+ port = server.addr[1]
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ ssl_server = OpenSSL::SSL::SSLServer.new(server, ctx)
+ ths = Thread.start do
+ begin
+ sock = ssl_server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0001 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ end
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
+ end
+ end
+ begin
+ begin
+ imap = yield(port)
+ imap.logout
+ ensure
+ imap.disconnect if imap
+ end
+ ensure
+ ssl_server.close
+ ths.join
+ end
+ end
+
+ def starttls_test
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("RUBY0001 OK completed\r\n")
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ sock.sync_close = true
+ sock.accept
+ 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 = yield(port)
+ imap.logout if !imap.disconnected?
+ ensure
+ imap.disconnect if imap && !imap.disconnected?
+ end
+ end
+
+ def create_tcp_server
+ return TCPServer.new(SERVER_ADDR, 0)
+ end
+end
diff --git a/jni/ruby/test/net/imap/test_imap_response_parser.rb b/jni/ruby/test/net/imap/test_imap_response_parser.rb
new file mode 100644
index 0000000..1612d78
--- /dev/null
+++ b/jni/ruby/test/net/imap/test_imap_response_parser.rb
@@ -0,0 +1,292 @@
+require "net/imap"
+require "test/unit"
+
+class IMAPResponseParserTest < Test::Unit::TestCase
+ def setup
+ @do_not_reverse_lookup = Socket.do_not_reverse_lookup
+ Socket.do_not_reverse_lookup = true
+ if Net::IMAP.respond_to?(:max_flag_count)
+ @max_flag_count = Net::IMAP.max_flag_count
+ Net::IMAP.max_flag_count = 3
+ end
+ end
+
+ def teardown
+ Socket.do_not_reverse_lookup = @do_not_reverse_lookup
+ if Net::IMAP.respond_to?(:max_flag_count)
+ Net::IMAP.max_flag_count = @max_flag_count
+ end
+ end
+
+ def test_flag_list_safe
+ parser = Net::IMAP::ResponseParser.new
+ response = lambda {
+ $SAFE = 1
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* LIST (\\HasChildren) "." "INBOX"
+EOF
+ }.call
+ assert_equal [:Haschildren], response.data.attr
+ end
+
+ def test_flag_list_too_many_flags
+ parser = Net::IMAP::ResponseParser.new
+ assert_nothing_raised do
+ 3.times do |i|
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* LIST (\\Foo#{i}) "." "INBOX"
+EOF
+ end
+ end
+ assert_raise(Net::IMAP::FlagCountError) do
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* LIST (\\Foo3) "." "INBOX"
+EOF
+ end
+ end
+
+ def test_flag_list_many_same_flags
+ parser = Net::IMAP::ResponseParser.new
+ assert_nothing_raised do
+ 100.times do
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* LIST (\\Foo) "." "INBOX"
+EOF
+ end
+ end
+ end
+
+ def test_flag_xlist_inbox
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* XLIST (\\Inbox) "." "INBOX"
+EOF
+ assert_equal [:Inbox], response.data.attr
+ end
+
+ def test_resp_text_code
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* OK [CLOSED] Previous mailbox closed.
+EOF
+ assert_equal "CLOSED", response.data.code.name
+ end
+
+ def test_search_response
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH
+EOF
+ assert_equal [], response.data
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH 1
+EOF
+ assert_equal [1], response.data
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH 1 2 3
+EOF
+ assert_equal [1, 2, 3], response.data
+ end
+
+ def test_search_response_of_yahoo
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH 1\s
+EOF
+ assert_equal [1], response.data
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH 1 2 3\s
+EOF
+ assert_equal [1, 2, 3], response.data
+ end
+
+ def test_msg_att_extra_space
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 1 FETCH (UID 92285)
+EOF
+ assert_equal 92285, response.data.attr["UID"]
+
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 1 FETCH (UID 92285 )
+EOF
+ assert_equal 92285, response.data.attr["UID"]
+ end
+
+ def test_msg_att_parse_error
+ parser = Net::IMAP::ResponseParser.new
+ e = assert_raise(Net::IMAP::ResponseParseError) {
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 123 FETCH (UNKNOWN 92285)
+EOF
+ }
+ assert_match(/ for \{123\}/, e.message)
+ end
+
+ def test_msg_att_rfc822_text
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 123 FETCH (RFC822 {5}
+foo
+)
+EOF
+ assert_equal("foo\r\n", response.data.attr["RFC822"])
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 123 FETCH (RFC822[] {5}
+foo
+)
+EOF
+ assert_equal("foo\r\n", response.data.attr["RFC822"])
+ end
+
+ # [Bug #6397] [ruby-core:44849]
+ def test_body_type_attachment
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 980 FETCH (UID 2862 BODYSTRUCTURE ((("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 416 21 NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 1493 32 NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "Boundary_(ID_IaecgfnXwG5bn3x8lIeGIQ)") NIL NIL)("MESSAGE" "RFC822" ("NAME" "Fw_ ____ _____ ____.eml") NIL NIL "7BIT" 1980088 NIL ("ATTACHMENT" ("FILENAME" "Fw_ ____ _____ ____.eml")) NIL) "MIXED" ("BOUNDARY" "Boundary_(ID_eDdLc/j0mBIzIlR191pHjA)") NIL NIL))
+EOF
+ assert_equal("Fw_ ____ _____ ____.eml",
+ response.data.attr["BODYSTRUCTURE"].parts[1].body.param["FILENAME"])
+ end
+
+ def assert_parseable(s)
+ parser = Net::IMAP::ResponseParser.new
+ parser.parse(s.gsub(/\n/, "\r\n").taint)
+ end
+
+ # [Bug #7146]
+ def test_msg_delivery_status
+ # This was part of a larger response that caused crashes, but this was the
+ # minimal test case to demonstrate it
+ assert_parseable <<EOF
+* 4902 FETCH (BODY (("MESSAGE" "DELIVERY-STATUS" NIL NIL NIL "7BIT" 324) "REPORT"))
+EOF
+ end
+
+ # [Bug #7147]
+ def test_msg_with_message_rfc822_attachment
+ assert_parseable <<EOF
+* 5441 FETCH (BODY ((("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 69 1)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 455 12) "ALTERNATIVE")("MESSAGE" "RFC822" ("NAME" "ATT00026.eml") NIL NIL "7BIT" 4079755) "MIXED"))
+EOF
+ end
+
+ # [Bug #7153]
+ def test_msg_body_mixed
+ assert_parseable <<EOF
+* 1038 FETCH (BODY ("MIXED"))
+EOF
+ end
+
+ # [Bug #8167]
+ def test_msg_delivery_status_with_extra_data
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 29021 FETCH (RFC822.SIZE 3162 UID 113622 RFC822.HEADER {1155}
+Return-path: <>
+Envelope-to: info@xxxxxxxx.si
+Delivery-date: Tue, 26 Mar 2013 12:42:58 +0100
+Received: from mail by xxxx.xxxxxxxxxxx.net with spam-scanned (Exim 4.76)
+ id 1UKSHI-000Cwl-AR
+ for info@xxxxxxxx.si; Tue, 26 Mar 2013 12:42:58 +0100
+X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on xxxx.xxxxxxxxxxx.net
+X-Spam-Level: **
+X-Spam-Status: No, score=2.1 required=7.0 tests=DKIM_ADSP_NXDOMAIN,RDNS_NONE
+ autolearn=no version=3.3.1
+Received: from [xx.xxx.xxx.xx] (port=56890 helo=xxxxxx.localdomain)
+ by xxxx.xxxxxxxxxxx.net with esmtp (Exim 4.76)
+ id 1UKSHI-000Cwi-9j
+ for info@xxxxxxxx.si; Tue, 26 Mar 2013 12:42:56 +0100
+Received: by xxxxxx.localdomain (Postfix)
+ id 72725BEA64A; Tue, 26 Mar 2013 12:42:55 +0100 (CET)
+Date: Tue, 26 Mar 2013 12:42:55 +0100 (CET)
+From: MAILER-DAEMON@xxxxxx.localdomain (Mail Delivery System)
+Subject: Undelivered Mail Returned to Sender
+To: info@xxxxxxxx.si
+Auto-Submitted: auto-replied
+MIME-Version: 1.0
+Content-Type: multipart/report; report-type=delivery-status;
+ boundary="27797BEA649.1364298175/xxxxxx.localdomain"
+Message-Id: <20130326114255.72725BEA64A@xxxxxx.localdomain>
+
+ BODYSTRUCTURE (("text" "plain" ("charset" "us-ascii") NIL "Notification" "7bit" 510 14 NIL NIL NIL NIL)("message" "delivery-status" NIL NIL "Delivery report" "7bit" 410 NIL NIL NIL NIL)("text" "rfc822-headers" ("charset" "us-ascii") NIL "Undelivered Message Headers" "7bit" 612 15 NIL NIL NIL NIL) "report" ("report-type" "delivery-status" "boundary" "27797BEA649.1364298175/xxxxxx.localdomain") NIL NIL NIL))
+EOF
+ delivery_status = response.data.attr["BODYSTRUCTURE"].parts[1]
+ assert_equal("MESSAGE", delivery_status.media_type)
+ assert_equal("DELIVERY-STATUS", delivery_status.subtype)
+ assert_equal(nil, delivery_status.param)
+ assert_equal(nil, delivery_status.content_id)
+ assert_equal("Delivery report", delivery_status.description)
+ assert_equal("7BIT", delivery_status.encoding)
+ assert_equal(410, delivery_status.size)
+ end
+
+ # [Bug #8281]
+ def test_acl
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* ACL "INBOX/share" "imshare2copy1366146467@xxxxxxxxxxxxxxxxxx.com" lrswickxteda
+EOF
+ assert_equal("ACL", response.name)
+ assert_equal(1, response.data.length)
+ assert_equal("INBOX/share", response.data[0].mailbox)
+ assert_equal("imshare2copy1366146467@xxxxxxxxxxxxxxxxxx.com",
+ response.data[0].user)
+ assert_equal("lrswickxteda", response.data[0].rights)
+ end
+
+ # [Bug #8415]
+ def test_capability
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* CAPABILITY st11p00mm-iscream009 1Q49 XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN\r\n")
+ assert_equal("CAPABILITY", response.name)
+ assert_equal("AUTH=PLAIN", response.data.last)
+ response = parser.parse("* CAPABILITY st11p00mm-iscream009 1Q49 XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN \r\n")
+ assert_equal("CAPABILITY", response.name)
+ assert_equal("AUTH=PLAIN", response.data.last)
+ end
+
+ def test_mixed_boundry
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* 2688 FETCH (UID 179161 BODYSTRUCTURE (" \
+ "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"iso-8859-1\") NIL NIL \"QUOTED-PRINTABLE\" 200 4 NIL NIL NIL)" \
+ "(\"MESSAGE\" \"DELIVERY-STATUS\" NIL NIL NIL \"7BIT\" 318 NIL NIL NIL)" \
+ "(\"MESSAGE\" \"RFC822\" NIL NIL NIL \"7BIT\" 2177" \
+ " (\"Tue, 11 May 2010 18:28:16 -0400\" \"Re: Welcome letter\" (" \
+ "(\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
+ "((\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
+ "((\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
+ "((\"Doretha\" NIL \"doretha.info\" \"xxxxxxxx.si\")) " \
+ "NIL NIL " \
+ "\"<AC1D15E06EA82F47BDE18E851CC32F330717704E@localdomain>\" " \
+ "\"<AANLkTikKMev1I73L2E7XLjRs67IHrEkb23f7ZPmD4S_9@localdomain>\")" \
+ " (\"MIXED\" (\"BOUNDARY\" \"000e0cd29212e3e06a0486590ae2\") NIL NIL)" \
+ " 37 NIL NIL NIL)" \
+ " \"REPORT\" (\"BOUNDARY\" \"16DuG.4XbaNOvCi.9ggvq.8Ipnyp3\" \"REPORT-TYPE\" \"delivery-status\") NIL NIL))\r\n")
+ empty_part = response.data.attr['BODYSTRUCTURE'].parts[2]
+ assert_equal(empty_part.lines, 37)
+ assert_equal(empty_part.body.media_type, 'MULTIPART')
+ assert_equal(empty_part.body.subtype, 'MIXED')
+ assert_equal(empty_part.body.param['BOUNDARY'], '000e0cd29212e3e06a0486590ae2')
+ end
+
+ # [Bug #10112]
+ def test_search_modseq
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* SEARCH 87216 87221 (MODSEQ 7667567)\r\n")
+ assert_equal("SEARCH", response.name)
+ assert_equal([87216, 87221], response.data)
+ end
+
+ # [Bug #11128]
+ def test_body_ext_mpart_without_lang
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* 4 FETCH (BODY (((\"text\" \"plain\" (\"charset\" \"utf-8\") NIL NIL \"7bit\" 257 9 NIL NIL NIL NIL)(\"text\" \"html\" (\"charset\" \"utf-8\") NIL NIL \"quoted-printable\" 655 9 NIL NIL NIL NIL) \"alternative\" (\"boundary\" \"001a1137a5047848dd05157ddaa1\") NIL)(\"application\" \"pdf\" (\"name\" \"test.xml\" \"x-apple-part-url\" \"9D00D9A2-98AB-4EFB-85BA-FB255F8BF3D7\") NIL NIL \"base64\" 4383638 NIL (\"attachment\" (\"filename\" \"test.xml\")) NIL NIL) \"mixed\" (\"boundary\" \"001a1137a5047848e405157ddaa3\") NIL))\r\n")
+ assert_equal("FETCH", response.name)
+ body = response.data.attr["BODY"]
+ assert_equal(nil, body.parts[0].disposition)
+ assert_equal(nil, body.parts[0].language)
+ assert_equal("ATTACHMENT", body.parts[1].disposition.dsp_type)
+ assert_equal("test.xml", body.parts[1].disposition.param["FILENAME"])
+ assert_equal(nil, body.parts[1].language)
+ end
+end
diff --git a/jni/ruby/test/net/pop/test_pop.rb b/jni/ruby/test/net/pop/test_pop.rb
new file mode 100644
index 0000000..7aa65a4
--- /dev/null
+++ b/jni/ruby/test/net/pop/test_pop.rb
@@ -0,0 +1,136 @@
+require 'net/pop'
+require 'test/unit'
+require 'digest/md5'
+
+class TestPOP < Test::Unit::TestCase
+ def setup
+ @users = {'user' => 'pass' }
+ @ok_user = 'user'
+ @stamp_base = "#{$$}.#{Time.now.to_i}@localhost"
+ end
+
+ def test_pop_auth_ok
+ pop_test(false) do |pop|
+ assert_instance_of Net::POP3, pop
+ assert_nothing_raised do
+ pop.start(@ok_user, @users[@ok_user])
+ end
+ end
+ end
+
+ def test_pop_auth_ng
+ pop_test(false) do |pop|
+ assert_instance_of Net::POP3, pop
+ assert_raise Net::POPAuthenticationError do
+ pop.start(@ok_user, 'bad password')
+ end
+ end
+ end
+
+ def test_apop_ok
+ pop_test(@stamp_base) do |pop|
+ assert_instance_of Net::APOP, pop
+ assert_nothing_raised do
+ pop.start(@ok_user, @users[@ok_user])
+ end
+ end
+ end
+
+ def test_apop_ng
+ pop_test(@stamp_base) do |pop|
+ assert_instance_of Net::APOP, pop
+ assert_raise Net::POPAuthenticationError do
+ pop.start(@ok_user, 'bad password')
+ end
+ end
+ end
+
+ def test_apop_invalid
+ pop_test("\x80"+@stamp_base) do |pop|
+ assert_instance_of Net::APOP, pop
+ assert_raise Net::POPAuthenticationError do
+ pop.start(@ok_user, @users[@ok_user])
+ end
+ end
+ end
+
+ def test_apop_invalid_at
+ pop_test(@stamp_base.sub('@', '.')) do |pop|
+ assert_instance_of Net::APOP, pop
+ assert_raise Net::POPAuthenticationError do
+ pop.start(@ok_user, @users[@ok_user])
+ end
+ end
+ end
+
+ def pop_test(apop=false)
+ host = 'localhost'
+ server = TCPServer.new(host, 0)
+ port = server.addr[1]
+ server_thread = Thread.start do
+ sock = server.accept
+ begin
+ pop_server_loop(sock, apop)
+ ensure
+ sock.close
+ end
+ end
+ client_thread = Thread.start do
+ begin
+ begin
+ pop = Net::POP3::APOP(apop).new(host, port)
+ #pop.set_debug_output $stderr
+ yield pop
+ ensure
+ begin
+ pop.finish
+ rescue IOError
+ raise unless $!.message == "POP session not yet started"
+ end
+ end
+ ensure
+ server.close
+ end
+ end
+ assert_join_threads([client_thread, server_thread])
+ end
+
+ def pop_server_loop(sock, apop)
+ if apop
+ sock.print "+OK ready <#{apop}>\r\n"
+ else
+ sock.print "+OK ready\r\n"
+ end
+ user = nil
+ while line = sock.gets
+ case line
+ when /^USER (.+)\r\n/
+ user = $1
+ if @users.key?(user)
+ sock.print "+OK\r\n"
+ else
+ sock.print "-ERR unknown user\r\n"
+ end
+ when /^PASS (.+)\r\n/
+ if @users[user] == $1
+ sock.print "+OK\r\n"
+ else
+ sock.print "-ERR invalid password\r\n"
+ end
+ when /^APOP (.+) (.+)\r\n/
+ user = $1
+ if apop && Digest::MD5.hexdigest("<#{apop}>#{@users[user]}") == $2
+ sock.print "+OK\r\n"
+ else
+ sock.print "-ERR authentication failed\r\n"
+ end
+ when /^QUIT/
+ sock.print "+OK bye\r\n"
+ return
+ else
+ sock.print "-ERR command not recognized\r\n"
+ return
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/net/protocol/test_protocol.rb b/jni/ruby/test/net/protocol/test_protocol.rb
new file mode 100644
index 0000000..4453422
--- /dev/null
+++ b/jni/ruby/test/net/protocol/test_protocol.rb
@@ -0,0 +1,28 @@
+require "test/unit"
+require "net/protocol"
+require "stringio"
+
+class TestProtocol < Test::Unit::TestCase
+ def test_should_properly_dot_stuff_period_with_no_endline
+ bug9627 = '[ruby-core:61441] [Bug #9627]'
+ sio = StringIO.new("")
+ imio = Net::InternetMessageIO.new(sio)
+ email = "To: bob@aol.com\nlook, a period with no endline\n."
+ imio.write_message(email)
+ assert_equal("To: bob@aol.com\r\nlook, a period with no endline\r\n..\r\n.\r\n", sio.string, bug9627)
+ end
+
+ def test_each_crlf_line
+ assert_output('', '') do
+ sio = StringIO.new("")
+ imio = Net::InternetMessageIO.new(sio)
+ assert_equal(23, imio.write_message("\u3042\r\u3044\n\u3046\r\n\u3048"))
+ assert_equal("\u3042\r\n\u3044\r\n\u3046\r\n\u3048\r\n.\r\n", sio.string)
+
+ sio = StringIO.new("")
+ imio = Net::InternetMessageIO.new(sio)
+ assert_equal(8, imio.write_message("\u3042\r"))
+ assert_equal("\u3042\r\n.\r\n", sio.string)
+ end
+ end
+end
diff --git a/jni/ruby/test/net/smtp/test_response.rb b/jni/ruby/test/net/smtp/test_response.rb
new file mode 100644
index 0000000..e7011e0
--- /dev/null
+++ b/jni/ruby/test/net/smtp/test_response.rb
@@ -0,0 +1,99 @@
+require 'net/smtp'
+require 'minitest/autorun'
+
+module Net
+ class SMTP
+ class TestResponse < MiniTest::Unit::TestCase
+ def test_capabilities
+ res = Response.parse("250-ubuntu-desktop\n250-PIPELINING\n250-SIZE 10240000\n250-VRFY\n250-ETRN\n250-STARTTLS\n250-ENHANCEDSTATUSCODES\n250 DSN\n")
+
+ capabilities = res.capabilities
+ %w{ PIPELINING SIZE VRFY STARTTLS ENHANCEDSTATUSCODES DSN}.each do |str|
+ assert capabilities.key?(str), str
+ end
+ end
+
+ def test_capabilities_default
+ res = Response.parse("250-ubuntu-desktop\n250-PIPELINING\n250 DSN\n")
+ assert_equal [], res.capabilities['PIPELINING']
+ end
+
+ def test_capabilities_value
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1234\n250 DSN\n")
+ assert_equal ['1234'], res.capabilities['SIZE']
+ end
+
+ def test_capabilities_multi
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert_equal %w{1 2 3}, res.capabilities['SIZE']
+ end
+
+ def test_bad_string
+ res = Response.parse("badstring")
+ assert_equal({}, res.capabilities)
+ end
+
+ def test_success?
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert res.success?
+ assert !res.continue?
+ end
+
+ # RFC 2821, Section 4.2.1
+ def test_continue?
+ res = Response.parse("3yz-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert !res.success?
+ assert res.continue?
+ end
+
+ def test_status_type_char
+ res = Response.parse("3yz-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert_equal '3', res.status_type_char
+
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert_equal '2', res.status_type_char
+ end
+
+ def test_message
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert_equal "250-ubuntu-desktop\n", res.message
+ end
+
+ def test_server_busy_exception
+ res = Response.parse("400 omg busy")
+ assert_equal Net::SMTPServerBusy, res.exception_class
+ res = Response.parse("410 omg busy")
+ assert_equal Net::SMTPServerBusy, res.exception_class
+ end
+
+ def test_syntax_error_exception
+ res = Response.parse("500 omg syntax error")
+ assert_equal Net::SMTPSyntaxError, res.exception_class
+
+ res = Response.parse("501 omg syntax error")
+ assert_equal Net::SMTPSyntaxError, res.exception_class
+ end
+
+ def test_authentication_exception
+ res = Response.parse("530 omg auth error")
+ assert_equal Net::SMTPAuthenticationError, res.exception_class
+
+ res = Response.parse("531 omg auth error")
+ assert_equal Net::SMTPAuthenticationError, res.exception_class
+ end
+
+ def test_fatal_error
+ res = Response.parse("510 omg fatal error")
+ assert_equal Net::SMTPFatalError, res.exception_class
+
+ res = Response.parse("511 omg fatal error")
+ assert_equal Net::SMTPFatalError, res.exception_class
+ end
+
+ def test_default_exception
+ res = Response.parse("250 omg fatal error")
+ assert_equal Net::SMTPUnknownError, res.exception_class
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/net/smtp/test_smtp.rb b/jni/ruby/test/net/smtp/test_smtp.rb
new file mode 100644
index 0000000..748b20d
--- /dev/null
+++ b/jni/ruby/test/net/smtp/test_smtp.rb
@@ -0,0 +1,54 @@
+require 'net/smtp'
+require 'stringio'
+require 'minitest/autorun'
+
+module Net
+ class TestSMTP < MiniTest::Unit::TestCase
+ class FakeSocket
+ def initialize out = "250 OK\n"
+ @write_io = StringIO.new
+ @read_io = StringIO.new out
+ end
+
+ def writeline line
+ @write_io.write "#{line}\r\n"
+ end
+
+ def readline
+ line = @read_io.gets
+ raise 'ran out of input' unless line
+ line.chop
+ end
+ end
+
+ def test_critical
+ smtp = Net::SMTP.new 'localhost', 25
+
+ assert_raises RuntimeError do
+ smtp.send :critical do
+ raise 'fail on purpose'
+ end
+ end
+
+ assert_kind_of Net::SMTP::Response, smtp.send(:critical),
+ '[Bug #9125]'
+ end
+
+ def test_esmtp
+ smtp = Net::SMTP.new 'localhost', 25
+ assert smtp.esmtp
+ assert smtp.esmtp?
+
+ smtp.esmtp = 'omg'
+ assert_equal 'omg', smtp.esmtp
+ assert_equal 'omg', smtp.esmtp?
+ end
+
+ def test_rset
+ smtp = Net::SMTP.new 'localhost', 25
+ smtp.instance_variable_set :@socket, FakeSocket.new
+
+ assert smtp.rset
+ end
+ end
+end
diff --git a/jni/ruby/test/net/smtp/test_ssl_socket.rb b/jni/ruby/test/net/smtp/test_ssl_socket.rb
new file mode 100644
index 0000000..dc8b03e
--- /dev/null
+++ b/jni/ruby/test/net/smtp/test_ssl_socket.rb
@@ -0,0 +1,91 @@
+require 'net/smtp'
+require 'minitest/autorun'
+
+module Net
+ class TestSSLSocket < MiniTest::Unit::TestCase
+ class MySMTP < SMTP
+ attr_accessor :fake_tcp, :fake_ssl
+
+ def tcp_socket address, port
+ fake_tcp
+ end
+
+ def ssl_socket socket, context
+ fake_ssl
+ end
+ end
+
+ require 'stringio'
+ class SSLSocket < StringIO
+ attr_accessor :sync_close, :connected, :closed
+
+ def initialize(*args)
+ @connected = false
+ @closed = true
+ super
+ end
+
+ def connect
+ self.connected = true
+ self.closed = false
+ end
+
+ def close
+ self.closed = true
+ end
+
+ def post_connection_check omg
+ end
+ end
+
+ def test_ssl_socket_close_on_post_connection_check_fail
+ tcp_socket = StringIO.new success_response
+
+ ssl_socket = SSLSocket.new.extend Module.new {
+ def post_connection_check omg
+ raise OpenSSL::SSL::SSLError, 'hostname was not match with the server certificate'
+ end
+ }
+
+ connection = MySMTP.new('localhost', 25)
+ connection.enable_starttls_auto
+ connection.fake_tcp = tcp_socket
+ connection.fake_ssl = ssl_socket
+
+ assert_raises(OpenSSL::SSL::SSLError) do
+ connection.start
+ end
+ assert_equal true, ssl_socket.closed
+ end
+
+ def test_ssl_socket_open_on_post_connection_check_success
+ tcp_socket = StringIO.new success_response
+
+ ssl_socket = SSLSocket.new success_response
+
+ connection = MySMTP.new('localhost', 25)
+ connection.enable_starttls_auto
+ connection.fake_tcp = tcp_socket
+ connection.fake_ssl = ssl_socket
+
+ connection.start
+ assert_equal false, ssl_socket.closed
+ end
+
+ def success_response
+ [
+ '220 smtp.example.com ESMTP Postfix',
+ "250-ubuntu-desktop",
+ "250-PIPELINING",
+ "250-SIZE 10240000",
+ "250-VRFY",
+ "250-ETRN",
+ "250-STARTTLS",
+ "250-ENHANCEDSTATUSCODES",
+ "250-8BITMIME",
+ "250 DSN",
+ "220 2.0.0 Ready to start TLS",
+ ].join("\r\n") + "\r\n"
+ end
+ end
+end if defined?(OpenSSL)