diff options
Diffstat (limited to 'test/net')
25 files changed, 2141 insertions, 2330 deletions
diff --git a/test/net/fixtures/Makefile b/test/net/fixtures/Makefile new file mode 100644 index 0000000000..88c232e3b6 --- /dev/null +++ b/test/net/fixtures/Makefile @@ -0,0 +1,15 @@ +all: + +regen_certs: + touch server.key + make server.crt + +cacert.pem: server.key + openssl req -new -x509 -days 3650 -key server.key -out cacert.pem -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 -subj "/C=JP/ST=Shimane/O=Ruby Core Team/OU=Ruby Test/CN=localhost" + +server.crt: server.csr cacert.pem + openssl x509 -days 3650 -CA cacert.pem -CAkey server.key -set_serial 00 -in server.csr -req -out server.crt + rm server.csr diff --git a/test/net/fixtures/cacert.pem b/test/net/fixtures/cacert.pem new file mode 100644 index 0000000000..24c83f1c65 --- /dev/null +++ b/test/net/fixtures/cacert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIUGMvHl3EhtKPKcgc3NQSAYfFuC+8wDQYJKoZIhvcNAQEL +BQAwgYwxCzAJBgNVBAYTAkpQMRAwDgYDVQQIDAdTaGltYW5lMRQwEgYDVQQHDAtN +YXR6LWUgY2l0eTEXMBUGA1UECgwOUnVieSBDb3JlIFRlYW0xFTATBgNVBAMMDFJ1 +YnkgVGVzdCBDQTElMCMGCSqGSIb3DQEJARYWc2VjdXJpdHlAcnVieS1sYW5nLm9y +ZzAeFw0yNDAxMDExMTQ3MjNaFw0zMzEyMjkxMTQ3MjNaMIGMMQswCQYDVQQGEwJK +UDEQMA4GA1UECAwHU2hpbWFuZTEUMBIGA1UEBwwLTWF0ei1lIGNpdHkxFzAVBgNV +BAoMDlJ1YnkgQ29yZSBUZWFtMRUwEwYDVQQDDAxSdWJ5IFRlc3QgQ0ExJTAjBgkq +hkiG9w0BCQEWFnNlY3VyaXR5QHJ1YnktbGFuZy5vcmcwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCw+egZQ6eumJKq3hfKfED4dE/tL4FI5sjqont9ABVI ++1GSqyi1bFBgsRjM0THllIdMbKmJtWwnKW8J+5OgNN8y6Xxv8JmM/Y5vQt2lis0f +qXmG8UTz0VTWdlAXXmhUs6lSADvAaIe4RVrCsZ97L3ZQTryY7JRVcbB4khUN3Gp0 +yg+801SXzoFTTa+UGIRLE66jH51aa5VXu99hnv1OiH8tQrjdi8mH6uG/icq4XuIe +NWMF32wHqIOOPvQcWV3M5D2vxJEj702Ku6k9OQXkAo17qRSEonWW4HtLbtmS8He1 +JNPc/n3dVUm+fM6NoDXPoLP7j55G9zKyqGtGAWXAj1MTAgMBAAGjUzBRMB0GA1Ud +DgQWBBSJGVleDvFp9cu9R+E0/OKYzGkwkTAfBgNVHSMEGDAWgBSJGVleDvFp9cu9 +R+E0/OKYzGkwkTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBl +8GLB8skAWlkSw/FwbUmEV3zyqu+p7PNP5YIYoZs0D74e7yVulGQ6PKMZH5hrZmHo +orFSQU+VUUirG8nDGj7Rzce8WeWBxsaDGC8CE2dq6nC6LuUwtbdMnBrH0LRWAz48 +jGFF3jHtVz8VsGfoZTZCjukWqNXvU6hETT9GsfU+PZqbqcTVRPH52+XgYayKdIbD +r97RM4X3+aXBHcUW0b76eyyi65RR/Xtvn8ioZt2AdX7T2tZzJyXJN3Hupp77s6Ui +AZR35SToHCZeTZD12YBvLBdaTPLZN7O/Q/aAO9ZiJaZ7SbFOjz813B2hxXab4Fob +2uJX6eMWTVxYK5D4M9lm +-----END CERTIFICATE----- diff --git a/test/net/fixtures/dhparams.pem b/test/net/fixtures/dhparams.pem new file mode 100644 index 0000000000..389285bf59 --- /dev/null +++ b/test/net/fixtures/dhparams.pem @@ -0,0 +1,29 @@ + DH Parameters: (2048 bit) + prime: + 00:ec:4e:a4:06:b6:22:ca:f9:8a:00:cc:d0:ee:2f: + 16:bf:05:64:f5:8f:fe:7f:c4:bb:b0:24:cd:ef:5d: + 8a:90:ad:dc:a9:dd:63:84:90:d8:25:ba:d8:78:d5: + 77:91:42:0a:84:fc:56:1e:13:9b:1c:aa:43:d5:1f: + 38:52:92:fe:b3:66:f9:e7:e8:8c:77:a1:a6:2f:b3: + 98:98:d2:13:fc:57:1c:2a:14:dc:bd:e6:9b:54:19: + 99:4f:ce:81:64:a6:32:7f:8e:61:50:5f:45:3a:e5: + 0c:f7:13:f3:b8:ad:d5:77:ca:09:42:f7:d8:30:27: + 7b:2c:f0:b4:b5:a0:04:96:34:0b:47:81:1d:7f:c1: + 3a:62:86:8e:7d:f8:13:7f:9a:b1:8b:09:23:9e:55: + 59:41:cd:f0:86:09:c4:b7:d1:69:54:cb:d0:f5:e9: + 27:c9:e1:81:e4:a1:df:6b:20:1c:df:e8:54:02:f2: + 37:fc:2a:f7:d5:b3:6f:79:7e:70:22:78:79:18:3c: + 75:14:68:4a:05:9f:ac:d4:7f:9a:79:db:9d:0a:6e: + ec:0a:04:70:bf:c9:4a:59:81:a2:1f:33:9b:4a:66: + bc:03:ce:8a:1b:e3:03:ec:ba:39:26:ab:90:dc:39: + 41:a1:d8:f7:20:3c:8f:af:12:2f:f7:a9:6f:44:f1: + 6d:03 + generator: 2 (0x2) +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY +JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab +VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6 +YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3 +1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD +7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg== +-----END DH PARAMETERS----- diff --git a/test/net/fixtures/server.crt b/test/net/fixtures/server.crt new file mode 100644 index 0000000000..5d2923795d --- /dev/null +++ b/test/net/fixtures/server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDYTCCAkkCAQAwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAkpQMRAwDgYD +VQQIDAdTaGltYW5lMRQwEgYDVQQHDAtNYXR6LWUgY2l0eTEXMBUGA1UECgwOUnVi +eSBDb3JlIFRlYW0xFTATBgNVBAMMDFJ1YnkgVGVzdCBDQTElMCMGCSqGSIb3DQEJ +ARYWc2VjdXJpdHlAcnVieS1sYW5nLm9yZzAeFw0yNDAxMDExMTQ3MjNaFw0zMzEy +MjkxMTQ3MjNaMGAxCzAJBgNVBAYTAkpQMRAwDgYDVQQIDAdTaGltYW5lMRcwFQYD +VQQKDA5SdWJ5IENvcmUgVGVhbTESMBAGA1UECwwJUnVieSBUZXN0MRIwEAYDVQQD +DAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw+egZ +Q6eumJKq3hfKfED4dE/tL4FI5sjqont9ABVI+1GSqyi1bFBgsRjM0THllIdMbKmJ +tWwnKW8J+5OgNN8y6Xxv8JmM/Y5vQt2lis0fqXmG8UTz0VTWdlAXXmhUs6lSADvA +aIe4RVrCsZ97L3ZQTryY7JRVcbB4khUN3Gp0yg+801SXzoFTTa+UGIRLE66jH51a +a5VXu99hnv1OiH8tQrjdi8mH6uG/icq4XuIeNWMF32wHqIOOPvQcWV3M5D2vxJEj +702Ku6k9OQXkAo17qRSEonWW4HtLbtmS8He1JNPc/n3dVUm+fM6NoDXPoLP7j55G +9zKyqGtGAWXAj1MTAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACtGNdj5TEtnJBYp +M+LhBeU3oNteldfycEm993gJp6ghWZFg23oX8fVmyEeJr/3Ca9bAgDqg0t9a0npN +oWKEY6wVKqcHgu3gSvThF5c9KhGbeDDmlTSVVNQmXWX0K2d4lS2cwZHH8mCm2mrY +PDqlEkSc7k4qSiqigdS8i80Yk+lDXWsm8CjsiC93qaRM7DnS0WPQR0c16S95oM6G +VklFKUSDAuFjw9aVWA/nahOucjn0w5fVW6lyIlkBslC1ChlaDgJmvhz+Ol3iMsE0 +kAmFNu2KKPVrpMWaBID49QwQTDyhetNLaVVFM88iUdA9JDoVMEuP1mm39JqyzHTu +uBrdP4Q= +-----END CERTIFICATE----- diff --git a/test/net/fixtures/server.key b/test/net/fixtures/server.key new file mode 100644 index 0000000000..6a83d5bcf4 --- /dev/null +++ b/test/net/fixtures/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAsPnoGUOnrpiSqt4XynxA+HRP7S+BSObI6qJ7fQAVSPtRkqso +tWxQYLEYzNEx5ZSHTGypibVsJylvCfuToDTfMul8b/CZjP2Ob0LdpYrNH6l5hvFE +89FU1nZQF15oVLOpUgA7wGiHuEVawrGfey92UE68mOyUVXGweJIVDdxqdMoPvNNU +l86BU02vlBiESxOuox+dWmuVV7vfYZ79Toh/LUK43YvJh+rhv4nKuF7iHjVjBd9s +B6iDjj70HFldzOQ9r8SRI+9NirupPTkF5AKNe6kUhKJ1luB7S27ZkvB3tSTT3P59 +3VVJvnzOjaA1z6Cz+4+eRvcysqhrRgFlwI9TEwIDAQABAoIBAEEYiyDP29vCzx/+ +dS3LqnI5BjUuJhXUnc6AWX/PCgVAO+8A+gZRgvct7PtZb0sM6P9ZcLrweomlGezI +FrL0/6xQaa8bBr/ve/a8155OgcjFo6fZEw3Dz7ra5fbSiPmu4/b/kvrg+Br1l77J +aun6uUAs1f5B9wW+vbR7tzbT/mxaUeDiBzKpe15GwcvbJtdIVMa2YErtRjc1/5B2 +BGVXyvlJv0SIlcIEMsHgnAFOp1ZgQ08aDzvilLq8XVMOahAhP1O2A3X8hKdXPyrx +IVWE9bS9ptTo+eF6eNl+d7htpKGEZHUxinoQpWEBTv+iOoHsVunkEJ3vjLP3lyI/ +fY0NQ1ECgYEA3RBXAjgvIys2gfU3keImF8e/TprLge1I2vbWmV2j6rZCg5r/AS0u +pii5CvJ5/T5vfJPNgPBy8B/yRDs+6PJO1GmnlhOkG9JAIPkv0RBZvR0PMBtbp6nT +Y3yo1lwamBVBfY6rc0sLTzosZh2aGoLzrHNMQFMGaauORzBFpY5lU50CgYEAzPHl +u5DI6Xgep1vr8QvCUuEesCOgJg8Yh1UqVoY/SmQh6MYAv1I9bLGwrb3WW/7kqIoD +fj0aQV5buVZI2loMomtU9KY5SFIsPV+JuUpy7/+VE01ZQM5FdY8wiYCQiVZYju9X +Wz5LxMNoz+gT7pwlLCsC4N+R8aoBk404aF1gum8CgYAJ7VTq7Zj4TFV7Soa/T1eE +k9y8a+kdoYk3BASpCHJ29M5R2KEA7YV9wrBklHTz8VzSTFTbKHEQ5W5csAhoL5Fo +qoHzFFi3Qx7MHESQb9qHyolHEMNx6QdsHUn7rlEnaTTyrXh3ifQtD6C0yTmFXUIS +CW9wKApOrnyKJ9nI0HcuZQKBgQCMtoV6e9VGX4AEfpuHvAAnMYQFgeBiYTkBKltQ +XwozhH63uMMomUmtSG87Sz1TmrXadjAhy8gsG6I0pWaN7QgBuFnzQ/HOkwTm+qKw +AsrZt4zeXNwsH7QXHEJCFnCmqw9QzEoZTrNtHJHpNboBuVnYcoueZEJrP8OnUG3r +UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0 +2riO4p6BaAdvzXjKeRrGNEKoHNBpOSfYCOM16NjL8hIZB1CaV3WbT5oY+jp7Mzd5 +7d56RZOE+ERK2uz/7JX9VSsM/LbH9pJibd4e8mikDS9ntciqOH/3 +-----END RSA PRIVATE KEY----- diff --git a/test/net/ftp/test_ftp.rb b/test/net/ftp/test_ftp.rb deleted file mode 100644 index f25e10747b..0000000000 --- a/test/net/ftp/test_ftp.rb +++ /dev/null @@ -1,813 +0,0 @@ -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 = [] - 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("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_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 = [] - 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("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 - 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("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 - begin - if sleep_time - sleep(sleep_time) - end - sock = server.accept - begin - yield(sock) - sock.shutdown(Socket::SHUT_WR) - sock.read_timeout = 1 - sock.read unless sock.eof? - ensure - sock.close - end - rescue - end - end - def server.port - addr[1] - end - return server - end -end diff --git a/test/net/http/test_buffered_io.rb b/test/net/http/test_buffered_io.rb index e24e7c1ed9..8c299ead03 100644 --- a/test/net/http/test_buffered_io.rb +++ b/test/net/http/test_buffered_io.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: false require 'test/unit' require 'net/http' require 'stringio' diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb index b83844377f..4e7fa22756 100644 --- a/test/net/http/test_http.rb +++ b/test/net/http/test_http.rb @@ -1,9 +1,9 @@ # coding: US-ASCII +# frozen_string_literal: false require 'test/unit' require 'net/http' require 'stringio' require_relative 'utils' -require_relative '../../ruby/envutil' class TestNetHTTP < Test::Unit::TestCase @@ -14,7 +14,7 @@ class TestNetHTTP < Test::Unit::TestCase proxy_class = Net::HTTP.Proxy 'proxy.example', 8000, 'user', 'pass' - refute_equal Net::HTTP, proxy_class + assert_not_equal Net::HTTP, proxy_class assert_operator proxy_class, :<, Net::HTTP @@ -23,9 +23,9 @@ class TestNetHTTP < Test::Unit::TestCase assert_equal 'user', proxy_class.proxy_user assert_equal 'pass', proxy_class.proxy_pass - http = proxy_class.new 'example' + http = proxy_class.new 'hostname.example' - refute http.proxy_from_env? + assert_not_predicate http, :proxy_from_env? proxy_class = Net::HTTP.Proxy 'proxy.example' @@ -34,7 +34,7 @@ class TestNetHTTP < Test::Unit::TestCase end def test_class_Proxy_from_ENV - clean_http_proxy_env do + TestNetHTTPUtils.clean_http_proxy_env do ENV['http_proxy'] = 'http://proxy.example:8000' # These are ignored on purpose. See Bug 4388 and Feature 6546 @@ -43,7 +43,7 @@ class TestNetHTTP < Test::Unit::TestCase proxy_class = Net::HTTP.Proxy :ENV - refute_equal Net::HTTP, proxy_class + assert_not_equal Net::HTTP, proxy_class assert_operator proxy_class, :<, Net::HTTP @@ -51,16 +51,43 @@ class TestNetHTTP < Test::Unit::TestCase assert_nil proxy_class.proxy_user assert_nil proxy_class.proxy_pass - refute_equal 8000, proxy_class.proxy_port + assert_not_equal 8000, proxy_class.proxy_port - http = proxy_class.new 'example' + http = proxy_class.new 'hostname.example' assert http.proxy_from_env? end end + def test_addr_port + http = Net::HTTP.new 'hostname.example', nil, nil + addr_port = http.__send__ :addr_port + assert_equal 'hostname.example', addr_port + + http.use_ssl = true + addr_port = http.__send__ :addr_port + assert_equal 'hostname.example:80', addr_port + + http = Net::HTTP.new '203.0.113.1', nil, nil + addr_port = http.__send__ :addr_port + assert_equal '203.0.113.1', addr_port + + http.use_ssl = true + addr_port = http.__send__ :addr_port + assert_equal '203.0.113.1:80', addr_port + + http = Net::HTTP.new '2001:db8::1', nil, nil + addr_port = http.__send__ :addr_port + assert_equal '[2001:db8::1]', addr_port + + http.use_ssl = true + addr_port = http.__send__ :addr_port + assert_equal '[2001:db8::1]:80', addr_port + + end + def test_edit_path - http = Net::HTTP.new 'example', nil, nil + http = Net::HTTP.new 'hostname.example', nil, nil edited = http.send :edit_path, '/path' @@ -74,11 +101,11 @@ class TestNetHTTP < Test::Unit::TestCase end def test_edit_path_proxy - http = Net::HTTP.new 'example', nil, 'proxy.example' + http = Net::HTTP.new 'hostname.example', nil, 'proxy.example' edited = http.send :edit_path, '/path' - assert_equal 'http://example/path', edited + assert_equal 'http://hostname.example/path', edited http.use_ssl = true @@ -88,114 +115,214 @@ class TestNetHTTP < Test::Unit::TestCase end def test_proxy_address - clean_http_proxy_env do - http = Net::HTTP.new 'example', nil, 'proxy.example' + TestNetHTTPUtils.clean_http_proxy_env do + http = Net::HTTP.new 'hostname.example', nil, 'proxy.example' assert_equal 'proxy.example', http.proxy_address - http = Net::HTTP.new 'example', nil + http = Net::HTTP.new 'hostname.example', nil assert_equal nil, http.proxy_address end end + def test_proxy_address_no_proxy + TestNetHTTPUtils.clean_http_proxy_env do + http = Net::HTTP.new 'hostname.example', nil, 'proxy.com', nil, nil, nil, 'example' + assert_nil http.proxy_address + + http = Net::HTTP.new '10.224.1.1', nil, 'proxy.com', nil, nil, nil, 'example,10.224.0.0/22' + assert_nil http.proxy_address + end + end + + def test_proxy_from_env_ENV + TestNetHTTPUtils.clean_http_proxy_env do + ENV['http_proxy'] = 'http://proxy.example:8000' + + assert_equal false, Net::HTTP.proxy_class? + http = Net::HTTP.new 'hostname.example' + + assert_equal true, http.proxy_from_env? + end + end + def test_proxy_address_ENV - clean_http_proxy_env do + TestNetHTTPUtils.clean_http_proxy_env do ENV['http_proxy'] = 'http://proxy.example:8000' - http = Net::HTTP.new 'example' + http = Net::HTTP.new 'hostname.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? + TestNetHTTPUtils.clean_http_proxy_env do + assert_equal false, Net::HTTP.new('hostname.example', nil, nil).proxy? end end def test_proxy_eh_ENV - clean_http_proxy_env do + TestNetHTTPUtils.clean_http_proxy_env do ENV['http_proxy'] = 'http://proxy.example:8000' - http = Net::HTTP.new 'example' + http = Net::HTTP.new 'hostname.example' assert_equal true, http.proxy? end end + def test_proxy_eh_ENV_with_user + TestNetHTTPUtils.clean_http_proxy_env do + ENV['http_proxy'] = 'http://foo:bar@proxy.example:8000' + + http = Net::HTTP.new 'hostname.example' + + assert_equal true, http.proxy? + assert_equal 'foo', http.proxy_user + assert_equal 'bar', http.proxy_pass + end + end + + def test_proxy_eh_ENV_with_urlencoded_user + TestNetHTTPUtils.clean_http_proxy_env do + ENV['http_proxy'] = 'http://Y%5CX:R%25S%5D%20%3FX@proxy.example:8000' + + http = Net::HTTP.new 'hostname.example' + + assert_equal true, http.proxy? + assert_equal "Y\\X", http.proxy_user + assert_equal "R%S] ?X", http.proxy_pass + end + end + def test_proxy_eh_ENV_none_set - clean_http_proxy_env do - assert_equal false, Net::HTTP.new('example').proxy? + TestNetHTTPUtils.clean_http_proxy_env do + assert_equal false, Net::HTTP.new('hostname.example').proxy? end end def test_proxy_eh_ENV_no_proxy - clean_http_proxy_env do + TestNetHTTPUtils.clean_http_proxy_env do ENV['http_proxy'] = 'http://proxy.example:8000' - ENV['no_proxy'] = 'example' + ENV['no_proxy'] = 'hostname.example' - assert_equal false, Net::HTTP.new('example').proxy? + assert_equal false, Net::HTTP.new('hostname.example').proxy? end end def test_proxy_port - clean_http_proxy_env do - http = Net::HTTP.new 'exmaple', nil, 'proxy.example' + TestNetHTTPUtils.clean_http_proxy_env do + http = Net::HTTP.new 'example', 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 + http = Net::HTTP.new 'example', nil, 'proxy.example', 8000 assert_equal 8000, http.proxy_port - http = Net::HTTP.new 'exmaple', nil + http = Net::HTTP.new 'example', nil assert_equal nil, http.proxy_port end end def test_proxy_port_ENV - clean_http_proxy_env do + TestNetHTTPUtils.clean_http_proxy_env do ENV['http_proxy'] = 'http://proxy.example:8000' - http = Net::HTTP.new 'example' + http = Net::HTTP.new 'hostname.example' assert_equal 8000, http.proxy_port end end def test_newobj - clean_http_proxy_env do + TestNetHTTPUtils.clean_http_proxy_env do ENV['http_proxy'] = 'http://proxy.example:8000' - http = Net::HTTP.newobj 'example' + http = Net::HTTP.newobj 'hostname.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 + def test_failure_message_includes_failed_domain_and_port + # hostname to be included in the error message + host = Struct.new(:to_s).new("<example>") + port = 2119 + # hack to let TCPSocket.open fail + def host.to_str; raise SocketError, "open failure"; end + uri = Struct.new(:scheme, :hostname, :port).new("http", host, port) + assert_raise_with_message(SocketError, /#{host}:#{port}/) do + TestNetHTTPUtils.clean_http_proxy_env{ Net::HTTP.get(uri) } end + end + + def test_default_configuration + Net::HTTP.default_configuration = { open_timeout: 5 } + http = Net::HTTP.new 'hostname.example' + assert_equal 5, http.open_timeout + assert_equal 60, http.read_timeout - yield + http.open_timeout = 10 + assert_equal 10, http.open_timeout ensure - orig.each do |key, value| - ENV[key] = value - end + Net::HTTP.default_configuration = nil end end module TestNetHTTP_version_1_1_methods + def test_s_start + begin + h = Net::HTTP.start(config('host'), config('port')) + ensure + h&.finish + end + assert_equal config('host'), h.address + assert_equal config('port'), h.port + assert_equal true, h.instance_variable_get(:@proxy_from_env) + + begin + h = Net::HTTP.start(config('host'), config('port'), :ENV) + ensure + h&.finish + end + assert_equal config('host'), h.address + assert_equal config('port'), h.port + assert_equal true, h.instance_variable_get(:@proxy_from_env) + + begin + h = Net::HTTP.start(config('host'), config('port'), nil) + ensure + h&.finish + end + assert_equal config('host'), h.address + assert_equal config('port'), h.port + assert_equal false, h.instance_variable_get(:@proxy_from_env) + end + def test_s_get assert_equal $test_net_http_data, Net::HTTP.get(config('host'), '/', config('port')) + + assert_equal $test_net_http_data, Net::HTTP.get( + URI.parse("http://#{config('host')}:#{config('port')}") + ) + assert_equal $test_net_http_data, Net::HTTP.get( + URI.parse("http://#{config('host')}:#{config('port')}"), "Accept" => "text/plain" + ) + end + + def test_s_get_response + res = Net::HTTP.get_response( + URI.parse("http://#{config('host')}:#{config('port')}") + ) + assert_equal "application/octet-stream", res["Content-Type"] + assert_equal $test_net_http_data, res.body + + res = Net::HTTP.get_response( + URI.parse("http://#{config('host')}:#{config('port')}"), "Accept" => "text/plain" + ) + assert_equal "text/plain", res["Content-Type"] + assert_equal $test_net_http_data, res.body end def test_head @@ -233,7 +360,7 @@ module TestNetHTTP_version_1_1_methods http.get('/', { 'User-Agent' => 'test' }.freeze) } - assert res.decode_content, '[Bug #7924]' + assert res.decode_content, '[Bug #7924]' if Net::HTTP::HAVE_ZLIB end def _test_get__iter(http) @@ -277,6 +404,7 @@ module TestNetHTTP_version_1_1_methods end } assert_equal 1, i + @log_tester = nil # server may encount ECONNRESET end def test_get__implicit_start @@ -291,6 +419,17 @@ module TestNetHTTP_version_1_1_methods assert_equal $test_net_http_data, res.body end + def test_get__crlf + start {|http| + assert_raise(ArgumentError) do + http.get("\r") + end + assert_raise(ArgumentError) do + http.get("\n") + end + } + end + def test_get2 start {|http| http.get2('/') {|res| @@ -315,7 +454,11 @@ module TestNetHTTP_version_1_1_methods def test_post start {|http| _test_post__base http + } + start {|http| _test_post__file http + } + start {|http| _test_post__no_data http } end @@ -349,6 +492,23 @@ module TestNetHTTP_version_1_1_methods end end + def test_s_post + url = "http://#{config('host')}:#{config('port')}/?q=a" + res = Net::HTTP.post( + URI.parse(url), + "a=x") + assert_equal "application/octet-stream", res["Content-Type"] + assert_equal "a=x", res.body + assert_equal url, res["X-request-uri"] + + res = Net::HTTP.post( + URI.parse(url), + "hello world", + "Content-Type" => "text/plain; charset=US-ASCII") + assert_equal "text/plain; charset=US-ASCII", res["Content-Type"] + assert_equal "hello world", res.body + end + def test_s_post_form url = "http://#{config('host')}:#{config('port')}/" res = Net::HTTP.post_form( @@ -394,24 +554,79 @@ module TestNetHTTP_version_1_1_methods assert_equal data, res.entity end + def test_timeout_during_HTTP_session_write + 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.write_timeout = EnvUtil.apply_timeout_scale(0.01) + conn.read_timeout = EnvUtil.apply_timeout_scale(0.01) if windows? + conn.open_timeout = EnvUtil.apply_timeout_scale(1) + + th = Thread.new do + err = !windows? ? Net::WriteTimeout : Net::ReadTimeout + assert_raise(err) do + conn.post('/', "a"*50_000_000) + end + end + assert th.join(EnvUtil.apply_timeout_scale(10)) + } + ensure + th&.kill + th&.join + end + + def test_timeout_during_non_chunked_streamed_HTTP_session_write + 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.write_timeout = EnvUtil.apply_timeout_scale(0.01) + conn.read_timeout = EnvUtil.apply_timeout_scale(0.01) if windows? + conn.open_timeout = EnvUtil.apply_timeout_scale(1) + + req = Net::HTTP::Post.new('/') + data = "a"*50_000_000 + req.content_length = data.size + req['Content-Type'] = 'application/x-www-form-urlencoded' + req.body_stream = StringIO.new(data) + + th = Thread.new do + assert_raise(Net::WriteTimeout) { conn.request(req) } + end + assert th.join(10) + } + ensure + th&.kill + th&.join + 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.01 + conn.read_timeout = EnvUtil.apply_timeout_scale(0.01) + conn.open_timeout = EnvUtil.apply_timeout_scale(1) th = Thread.new do assert_raise(Net::ReadTimeout) { conn.get('/') } end - assert th.join(10), bug4246 + assert th.join(EnvUtil.apply_timeout_scale(10)), bug4246 } + ensure + th.kill + th.join end end @@ -426,10 +641,12 @@ module TestNetHTTP_version_1_2_methods # _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 } + start {|http| + _test_request__stream_body http + } end def _test_request__GET(http) @@ -460,7 +677,7 @@ module TestNetHTTP_version_1_2_methods assert_equal $test_net_http_data.size, res.body.size assert_equal $test_net_http_data, res.body - refute res.decode_content, 'Bug #7831' + assert_not_predicate res, :decode_content, 'Bug #7831' if Net::HTTP::HAVE_ZLIB } end @@ -527,53 +744,54 @@ module TestNetHTTP_version_1_2_methods end def _test_request__path(http) - uri = URI 'https://example/' + uri = URI 'https://hostname.example/' req = Net::HTTP::Get.new('/') res = http.request(req) assert_kind_of URI::Generic, req.uri - refute_equal uri, req.uri + assert_not_equal uri, req.uri assert_equal uri, res.uri - refute_same uri, req.uri - refute_same req.uri, res.uri + assert_not_same uri, req.uri + assert_not_same req.uri, res.uri end def _test_request__uri(http) - uri = URI 'https://example/' + uri = URI 'https://hostname.example/' req = Net::HTTP::Get.new(uri) res = http.request(req) assert_kind_of URI::Generic, req.uri - refute_equal uri, req.uri + assert_not_equal uri, req.uri assert_equal req.uri, res.uri - refute_same uri, req.uri - refute_same req.uri, res.uri + assert_not_same uri, req.uri + assert_not_same req.uri, res.uri end def _test_request__uri_host(http) - uri = URI 'http://example/' + uri = URI 'http://other.example/' req = Net::HTTP::Get.new(uri) - req['host'] = 'other.example' + req['host'] = 'hostname.example' res = http.request(req) assert_kind_of URI::Generic, req.uri - assert_equal URI("http://example:#{http.port}"), res.uri + assert_equal URI("http://hostname.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 @@ -588,6 +806,16 @@ module TestNetHTTP_version_1_2_methods 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') @@ -629,7 +857,13 @@ Content-Type: application/octet-stream __EOM__ start {|http| _test_set_form_urlencoded(http, data.reject{|k,v|!v.is_a?(String)}) + } + start {|http| + @server.mount('/', lambda {|req, res| res.body = req.body }) _test_set_form_multipart(http, false, data, expected) + } + start {|http| + @server.mount('/', lambda {|req, res| res.body = req.body }) _test_set_form_multipart(http, true, data, expected) } } @@ -673,6 +907,7 @@ __EOM__ expected.sub!(/<filename>/, filename) expected.sub!(/<data>/, $test_net_http_data) start {|http| + @server.mount('/', lambda {|req, res| res.body = req.body }) data.each{|k,v|v.rewind rescue nil} req = Net::HTTP::Post.new('/') req.set_form(data, 'multipart/form-data') @@ -688,10 +923,11 @@ __EOM__ 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) + # TODO: test with chunked + # data.each{|k,v|v.rewind rescue nil} + # req['Transfer-Encoding'] = 'chunked' + # res = http.request req + # assert_equal(expected, res.body) } } end @@ -700,7 +936,6 @@ end class TestNetHTTP_v1_2 < Test::Unit::TestCase CONFIG = { 'host' => '127.0.0.1', - 'port' => 0, 'proxy_host' => nil, 'proxy_port' => nil, } @@ -713,12 +948,22 @@ class TestNetHTTP_v1_2 < Test::Unit::TestCase Net::HTTP.version_1_2 super end + + def test_send_large_POST_request + start {|http| + data = ' '*6000000 + 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 end class TestNetHTTP_v1_2_chunked < Test::Unit::TestCase CONFIG = { 'host' => '127.0.0.1', - 'port' => 0, 'proxy_host' => nil, 'proxy_port' => nil, 'chunked' => true, @@ -749,7 +994,6 @@ end class TestNetHTTPContinue < Test::Unit::TestCase CONFIG = { 'host' => '127.0.0.1', - 'port' => 0, 'proxy_host' => nil, 'proxy_port' => nil, 'chunked' => true, @@ -762,7 +1006,7 @@ class TestNetHTTPContinue < Test::Unit::TestCase end def mount_proc(&block) - @server.mount('/continue', WEBrick::HTTPServlet::ProcHandler.new(block.to_proc)) + @server.mount('/continue', block.to_proc) end def test_expect_continue @@ -814,6 +1058,22 @@ class TestNetHTTPContinue < Test::Unit::TestCase assert_not_match(/HTTP\/1.1 100 continue/, @debug.string) end + def test_expect_continue_error_before_body + @log_tester = nil + mount_proc {|req, res| + raise TestNetHTTPUtils::Forbidden + } + start {|http| + uheader = {'content-type' => 'application/x-www-form-urlencoded', 'content-length' => '5', 'expect' => '100-continue'} + http.continue_timeout = 1 # allow the server to respond before sending + http.request_post('/continue', 'data', uheader) {|res| + assert_equal(res.code, '403') + } + } + 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 @@ -831,10 +1091,43 @@ class TestNetHTTPContinue < Test::Unit::TestCase end end +class TestNetHTTPSwitchingProtocols < 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', block.to_proc) + end + + def test_info + mount_proc {|req, res| + req.instance_variable_get(:@socket) << "HTTP/1.1 101 Switching Protocols\r\n\r\n" + res.body = req.query['body'] + } + start {|http| + http.continue_timeout = 0.2 + http.request_post('/continue', 'body=BODY', + 'content-type' => 'application/x-www-form-urlencoded') {|res| + assert_equal('BODY', res.read_body) + } + } + assert_match(/HTTP\/1.1 101 Switching Protocols/, @debug.string) + end +end + class TestNetHTTPKeepAlive < Test::Unit::TestCase CONFIG = { 'host' => '127.0.0.1', - 'port' => 0, 'proxy_host' => nil, 'proxy_port' => nil, 'RequestTimeout' => 1, @@ -857,6 +1150,23 @@ class TestNetHTTPKeepAlive < Test::Unit::TestCase } end + def test_server_closed_connection_auto_reconnect + 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 + assert_nothing_raised { + # Net::HTTP should detect the closed connection before attempting the + # request, since post requests cannot be retried. + res = http.post('/', 'query=foo', 'content-type' => 'application/x-www-form-urlencoded') + } + 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('/') @@ -870,13 +1180,99 @@ class TestNetHTTPKeepAlive < Test::Unit::TestCase } end + def test_keep_alive_reset_on_new_connection + # Using debug log output on accepting connection: + # + # "[2021-04-29 20:36:46] DEBUG accept: 127.0.0.1:50674\n" + @log_tester = nil + @logger_level = :debug + + start {|http| + res = http.get('/') + http.keep_alive_timeout = 1 + assert_kind_of Net::HTTPResponse, res + assert_kind_of String, res.body + http.finish + assert_equal 1, @log.grep(/accept/i).size + + sleep 1.5 + http.start + res = http.get('/') + assert_kind_of Net::HTTPResponse, res + assert_kind_of String, res.body + assert_equal 2, @log.grep(/accept/i).size + } + end + + class MockSocket + attr_reader :count + def initialize(success_after: nil) + @success_after = success_after + @count = 0 + end + def close + end + def closed? + end + def write(_) + end + def readline + @count += 1 + if @success_after && @success_after <= @count + "HTTP/1.1 200 OK" + else + raise Errno::ECONNRESET + end + end + def readuntil(*_) + "" + end + def read_all(_) + end + end + + def test_http_retry_success + start {|http| + socket = MockSocket.new(success_after: 10) + http.instance_variable_get(:@socket).close + http.instance_variable_set(:@socket, socket) + assert_equal 0, socket.count + http.max_retries = 10 + res = http.get('/') + assert_equal 10, socket.count + assert_kind_of Net::HTTPResponse, res + assert_kind_of String, res.body + } + end + + def test_http_retry_failed + start {|http| + socket = MockSocket.new + http.instance_variable_get(:@socket).close + http.instance_variable_set(:@socket, socket) + http.max_retries = 10 + assert_raise(Errno::ECONNRESET){ http.get('/') } + assert_equal 11, socket.count + } + end + + def test_http_retry_failed_with_block + start {|http| + http.max_retries = 10 + called = 0 + assert_raise(Errno::ECONNRESET){ http.get('/'){called += 1; raise Errno::ECONNRESET} } + assert_equal 1, called + } + @log_tester = nil + end + def test_keep_alive_server_close def @server.run(sock) sock.close end start {|http| - assert_raises(EOFError, Errno::ECONNRESET, IOError) { + assert_raise(EOFError, Errno::ECONNRESET, IOError) { http.get('/') } } @@ -886,7 +1282,6 @@ end class TestNetHTTPLocalBind < Test::Unit::TestCase CONFIG = { 'host' => 'localhost', - 'port' => 0, 'proxy_host' => nil, 'proxy_port' => nil, } @@ -910,7 +1305,9 @@ class TestNetHTTPLocalBind < Test::Unit::TestCase http = Net::HTTP.new(config('host'), config('port')) http.local_host = Addrinfo.tcp(config('host'), config('port')).ip_address - http.local_port = [*10000..20000].shuffle.first.to_s + 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) @@ -919,3 +1316,112 @@ class TestNetHTTPLocalBind < Test::Unit::TestCase end end +class TestNetHTTPForceEncoding < Test::Unit::TestCase + CONFIG = { + 'host' => 'localhost', + 'proxy_host' => nil, + 'proxy_port' => nil, + } + + include TestNetHTTPUtils + + def fe_request(force_enc, content_type=nil) + @server.mount_proc('/fe') do |req, res| + res['Content-Type'] = content_type if content_type + res.body = "hello\u1234" + end + + 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) + + http.response_body_encoding = force_enc + http.get('/fe') + end + + def test_response_body_encoding_false + res = fe_request(false) + assert_equal("hello\u1234".b, res.body) + assert_equal(Encoding::ASCII_8BIT, res.body.encoding) + end + + def test_response_body_encoding_true_without_content_type + res = fe_request(true) + assert_equal("hello\u1234".b, res.body) + assert_equal(Encoding::ASCII_8BIT, res.body.encoding) + end + + def test_response_body_encoding_true_with_content_type + res = fe_request(true, 'text/html; charset=utf-8') + assert_equal("hello\u1234", res.body) + assert_equal(Encoding::UTF_8, res.body.encoding) + end + + def test_response_body_encoding_string_without_content_type + res = fe_request('utf-8') + assert_equal("hello\u1234", res.body) + assert_equal(Encoding::UTF_8, res.body.encoding) + end + + def test_response_body_encoding_encoding_without_content_type + res = fe_request(Encoding::UTF_8) + assert_equal("hello\u1234", res.body) + assert_equal(Encoding::UTF_8, res.body.encoding) + end +end + +class TestNetHTTPPartialResponse < Test::Unit::TestCase + CONFIG = { + 'host' => '127.0.0.1', + 'proxy_host' => nil, + 'proxy_port' => nil, + } + + include TestNetHTTPUtils + + def test_partial_response + str = "0123456789" + @server.mount_proc('/') do |req, res| + res.status = 200 + res['Content-Type'] = 'text/plain' + + res.body = str + res['Content-Length'] = str.length + 1 + end + @server.mount_proc('/show_ip') { |req, res| res.body = req.remote_ip } + + http = Net::HTTP.new(config('host'), config('port')) + res = http.get('/') + assert_equal(str, res.body) + + http = Net::HTTP.new(config('host'), config('port')) + http.ignore_eof = false + assert_raise(EOFError) {http.get('/')} + end +end + +class TestNetHTTPInRactor < Test::Unit::TestCase + CONFIG = { + 'host' => '127.0.0.1', + 'proxy_host' => nil, + 'proxy_port' => nil, + } + + include TestNetHTTPUtils + + def test_get + assert_ractor(<<~RUBY, require: 'net/http') + expected = #{$test_net_http_data.dump}.b + ret = Ractor.new { + host = #{config('host').dump} + port = #{config('port')} + Net::HTTP.start(host, port) { |http| + res = http.get('/') + res.body + } + }.value + assert_equal expected, ret + RUBY + end +end if defined?(Ractor) && Ractor.method_defined?(:value) diff --git a/test/net/http/test_http_request.rb b/test/net/http/test_http_request.rb index 1dcb847c3f..9f5cf4f8f5 100644 --- a/test/net/http/test_http_request.rb +++ b/test/net/http/test_http_request.rb @@ -1,6 +1,6 @@ +# frozen_string_literal: false require 'net/http' require 'test/unit' -require 'stringio' class HTTPRequestTest < Test::Unit::TestCase @@ -8,8 +8,8 @@ class HTTPRequestTest < Test::Unit::TestCase req = Net::HTTP::Get.new '/' assert_equal 'GET', req.method - refute req.request_body_permitted? - assert req.response_body_permitted? + assert_not_predicate req, :request_body_permitted? + assert_predicate req, :response_body_permitted? expected = { 'accept' => %w[*/*], @@ -26,8 +26,8 @@ class HTTPRequestTest < Test::Unit::TestCase req = Net::HTTP::Get.new '/', 'Range' => 'bytes=0-9' assert_equal 'GET', req.method - refute req.request_body_permitted? - assert req.response_body_permitted? + assert_not_predicate req, :request_body_permitted? + assert_predicate req, :response_body_permitted? expected = { 'accept' => %w[*/*], @@ -42,12 +42,13 @@ class HTTPRequestTest < Test::Unit::TestCase req = Net::HTTP::Head.new '/' assert_equal 'HEAD', req.method - refute req.request_body_permitted? - refute req.response_body_permitted? + assert_not_predicate req, :request_body_permitted? + assert_not_predicate req, :response_body_permitted? expected = { - 'accept' => %w[*/*], - 'user-agent' => %w[Ruby], + 'accept' => %w[*/*], + "accept-encoding" => %w[gzip;q=1.0,deflate;q=0.6,identity;q=0.3], + 'user-agent' => %w[Ruby], } assert_equal expected, req.to_hash @@ -60,10 +61,35 @@ class HTTPRequestTest < Test::Unit::TestCase req2 = Net::HTTP::Get.new '/', 'accept-encoding' => 'identity' - refute req2.decode_content, - 'Bug #7381 - do not decode content if the user overrides' + assert_not_predicate req2, :decode_content, + 'Bug #7381 - do not decode content if the user overrides' end if Net::HTTP::HAVE_ZLIB + def test_initialize_GET_uri + req = Net::HTTP::Get.new(URI("http://example.com/foo")) + assert_equal "/foo", req.path + assert_equal "example.com", req['Host'] + + req = Net::HTTP::Get.new(URI("https://example.com/foo")) + assert_equal "/foo", req.path + assert_equal "example.com", req['Host'] + + req = Net::HTTP::Get.new(URI("https://203.0.113.1/foo")) + assert_equal "/foo", req.path + assert_equal "203.0.113.1", req['Host'] + + req = Net::HTTP::Get.new(URI("https://203.0.113.1:8000/foo")) + assert_equal "/foo", req.path + assert_equal "203.0.113.1:8000", req['Host'] + + req = Net::HTTP::Get.new(URI("https://[2001:db8::1]:8000/foo")) + assert_equal "/foo", req.path + assert_equal "[2001:db8::1]:8000", req['Host'] + + assert_raise(ArgumentError){ Net::HTTP::Get.new(URI("urn:ietf:rfc:7231")) } + assert_raise(ArgumentError){ Net::HTTP::Get.new(URI("http://")) } + end + def test_header_set req = Net::HTTP::Get.new '/' @@ -71,9 +97,29 @@ class HTTPRequestTest < Test::Unit::TestCase req['accept-encoding'] = 'identity' - refute req.decode_content, - 'Bug #7831 - do not decode content if the user overrides' + assert_not_predicate req, :decode_content, + 'Bug #7831 - do not decode content if the user overrides' end if Net::HTTP::HAVE_ZLIB + def test_update_uri + req = Net::HTTP::Get.new(URI.parse("http://203.0.113.1")) + req.update_uri("test", 8080, false) + assert_equal "203.0.113.1", req.uri.host + assert_equal 8080, req.uri.port + + req = Net::HTTP::Get.new(URI.parse("http://203.0.113.1:2020")) + req.update_uri("test", 8080, false) + assert_equal "203.0.113.1", req.uri.host + assert_equal 8080, req.uri.port + + req = Net::HTTP::Get.new(URI.parse("http://[2001:db8::1]")) + req.update_uri("test", 8080, false) + assert_equal "[2001:db8::1]", req.uri.host + assert_equal 8080, req.uri.port + + req = Net::HTTP::Get.new(URI.parse("http://[2001:db8::1]:2020")) + req.update_uri("test", 8080, false) + assert_equal "[2001:db8::1]", req.uri.host + assert_equal 8080, req.uri.port + end end - diff --git a/test/net/http/test_httpheader.rb b/test/net/http/test_httpheader.rb index 062387189d..69563168db 100644 --- a/test/net/http/test_httpheader.rb +++ b/test/net/http/test_httpheader.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: false require 'net/http' require 'test/unit' @@ -15,6 +16,30 @@ class HTTPHeaderTest < Test::Unit::TestCase @c = C.new end + def test_initialize + @c.initialize_http_header("foo"=>"abc") + assert_equal "abc", @c["foo"] + @c.initialize_http_header("foo"=>"abc", "bar"=>"xyz") + assert_equal "xyz", @c["bar"] + @c.initialize_http_header([["foo", "abc"]]) + assert_equal "abc", @c["foo"] + @c.initialize_http_header([["foo", "abc"], ["bar","xyz"]]) + assert_equal "xyz", @c["bar"] + assert_raise(NoMethodError){ @c.initialize_http_header("foo"=>[]) } + assert_raise(ArgumentError){ @c.initialize_http_header("foo"=>"a\nb") } + assert_raise(ArgumentError){ @c.initialize_http_header("foo"=>"a\rb") } + end + + def test_initialize_with_broken_coderange + error = RUBY_VERSION >= "3.2" ? Encoding::CompatibilityError : ArgumentError + assert_raise(error){ @c.initialize_http_header("foo"=>"a\xff") } + end + + def test_initialize_with_symbol + @c.initialize_http_header(foo: "abc") + assert_equal "abc", @c["foo"] + end + def test_size assert_equal 0, @c.size @c['a'] = 'a' @@ -39,6 +64,16 @@ class HTTPHeaderTest < Test::Unit::TestCase @c['aaA'] = 'aaa' @c['AAa'] = 'aaa' assert_equal 2, @c.length + + @c['aaa'] = ['aaa', ['bbb', [3]]] + assert_equal 2, @c.length + assert_equal ['aaa', 'bbb', '3'], @c.get_fields('aaa') + + @c['aaa'] = "aaa\xff" + assert_equal 2, @c.length + + assert_raise(ArgumentError){ @c['foo'] = "a\nb" } + assert_raise(ArgumentError){ @c['foo'] = ["a\nb"] } end def test_AREF @@ -64,6 +99,10 @@ class HTTPHeaderTest < Test::Unit::TestCase @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') + assert_raise(ArgumentError){ @c.add_field 'My-Header', "d\nd" } + @c.add_field 'My-Header', ['e', ["\xff", 7]] + assert_equal "a, b, c, d, d, e, \xff, 7", @c['My-Header'] + assert_equal ['a', 'b', 'c', 'd, d', 'e', "\xff", '7'], @c.get_fields('My-Header') end def test_get_fields @@ -81,6 +120,24 @@ class HTTPHeaderTest < Test::Unit::TestCase assert_equal ['test string'], @c.get_fields('my-header') end + class D; include Net::HTTPHeader; end + + def test_nil_variable_header + assert_nothing_raised do + assert_warning("#{__FILE__}:#{__LINE__+1}: warning: net/http: nil HTTP header: Authorization\n") do + D.new.initialize_http_header({Authorization: nil}) + end + end + end + + def test_duplicated_variable_header + assert_nothing_raised do + assert_warning("#{__FILE__}:#{__LINE__+1}: warning: net/http: duplicated HTTP header: Authorization\n") do + D.new.initialize_http_header({"AUTHORIZATION": "yes", "Authorization": "no"}) + end + end + end + def test_delete @c['My-Header'] = 'test' assert_equal 'test', @c['My-Header'] @@ -104,6 +161,12 @@ class HTTPHeaderTest < Test::Unit::TestCase assert_equal 'my-header', k assert_equal 'test', v end + e = @c.each + assert_equal 1, e.size + e.each do |k, v| + assert_equal 'my-header', k + assert_equal 'test', v + end end def test_each_key @@ -114,6 +177,26 @@ class HTTPHeaderTest < Test::Unit::TestCase @c.each_key do |k| assert_equal 'my-header', k end + e = @c.each_key + assert_equal 1, e.size + e.each do |k| + assert_equal 'my-header', k + end + end + + def test_each_capitalized_name + @c['my-header'] = 'test' + @c.each_capitalized_name do |k| + assert_equal 'My-Header', k + end + @c.each_capitalized_name do |k| + assert_equal 'My-Header', k + end + e = @c.each_capitalized_name + assert_equal 1, e.size + e.each do |k| + assert_equal 'My-Header', k + end end def test_each_value @@ -124,6 +207,11 @@ class HTTPHeaderTest < Test::Unit::TestCase @c.each_value do |v| assert_equal 'test', v end + e = @c.each_value + assert_equal 1, e.size + e.each do |v| + assert_equal 'test', v + end end def test_canonical_each @@ -132,6 +220,12 @@ class HTTPHeaderTest < Test::Unit::TestCase assert_equal 'My-Header', k assert_equal 'a, b', v end + e = @c.canonical_each + assert_equal 1, e.size + e.each do |k,v| + assert_equal 'My-Header', k + assert_equal 'a, b', v + end end def test_each_capitalized @@ -140,6 +234,26 @@ class HTTPHeaderTest < Test::Unit::TestCase assert_equal 'My-Header', k assert_equal 'a, b', v end + e = @c.each_capitalized + assert_equal 1, e.size + e.each do |k,v| + assert_equal 'My-Header', k + assert_equal 'a, b', v + end + end + + def test_each_capitalized_with_symbol + @c[:my_header] = ['a', 'b'] + @c.each_capitalized do |k,v| + assert_equal "My_header", k + assert_equal 'a, b', v + end + e = @c.each_capitalized + assert_equal 1, e.size + e.each do |k,v| + assert_equal 'My_header', k + assert_equal 'a, b', v + end end def test_key? @@ -198,6 +312,18 @@ class HTTPHeaderTest < Test::Unit::TestCase end def test_content_range + @c['Content-Range'] = "bytes 0-499/1000" + assert_equal 0..499, @c.content_range + @c['Content-Range'] = "bytes 1-500/1000" + assert_equal 1..500, @c.content_range + @c['Content-Range'] = "bytes 1-1/1000" + assert_equal 1..1, @c.content_range + @c['Content-Range'] = "tokens 1-1/1000" + assert_equal nil, @c.content_range + + try_invalid_content_range "invalid" + try_invalid_content_range "bytes 123-abc" + try_invalid_content_range "bytes abc-123" end def test_range_length @@ -207,6 +333,15 @@ class HTTPHeaderTest < Test::Unit::TestCase assert_equal 500, @c.range_length @c['Content-Range'] = "bytes 1-1/1000" assert_equal 1, @c.range_length + @c['Content-Range'] = "tokens 1-1/1000" + assert_equal nil, @c.range_length + + try_invalid_content_range "bytes 1-1/abc" + end + + def try_invalid_content_range(s) + @c['Content-Range'] = "#{s}" + assert_raise(Net::HTTPHeaderSyntaxError, s){ @c.content_range } end def test_chunked? diff --git a/test/net/http/test_httpresponse.rb b/test/net/http/test_httpresponse.rb index 974f8296cc..01281063cd 100644 --- a/test/net/http/test_httpresponse.rb +++ b/test/net/http/test_httpresponse.rb @@ -1,4 +1,5 @@ # coding: US-ASCII +# frozen_string_literal: false require 'net/http' require 'test/unit' require 'stringio' @@ -53,6 +54,241 @@ EOS assert_equal 'hello', body end + def test_read_body_body_encoding_false + body = "hello\u1234" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{body.bytesize} + +#{body} +EOS + + res = Net::HTTPResponse.read_new(io) + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal "hello\u1234".b, body + assert_equal Encoding::ASCII_8BIT, body.encoding + end + + def test_read_body_body_encoding_encoding + body = "hello\u1234" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{body.bytesize} + +#{body} +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = Encoding.find('utf-8') + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal "hello\u1234", body + assert_equal Encoding::UTF_8, body.encoding + end + + def test_read_body_body_encoding_string + body = "hello\u1234" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{body.bytesize} + +#{body} +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = 'utf-8' + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal "hello\u1234", body + assert_equal Encoding::UTF_8, body.encoding + end + + def test_read_body_body_encoding_true_without_content_type_header + body = "hello\u1234" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{body.bytesize} + +#{body} +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = true + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal "hello\u1234".b, body + assert_equal Encoding::ASCII_8BIT, body.encoding + end + + def test_read_body_body_encoding_true_with_utf8_content_type_header + body = "hello\u1234" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{body.bytesize} +Content-Type: text/plain; charset=utf-8 + +#{body} +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = true + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal "hello\u1234", body + assert_equal Encoding::UTF_8, body.encoding + end + + def test_read_body_body_encoding_true_with_iso_8859_1_content_type_header + body = "hello\u1234" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{body.bytesize} +Content-Type: text/plain; charset=iso-8859-1 + +#{body} +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = true + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal "hello\u1234".force_encoding("ISO-8859-1"), body + assert_equal Encoding::ISO_8859_1, body.encoding + end + + def test_read_body_body_encoding_true_with_utf8_meta_charset + res_body = "<html><meta charset=\"utf-8\">hello\u1234</html>" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{res_body.bytesize} +Content-Type: text/html + +#{res_body} +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = true + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal res_body, body + assert_equal Encoding::UTF_8, body.encoding + end + + def test_read_body_body_encoding_true_with_iso8859_1_meta_charset + res_body = "<html><meta charset=\"iso-8859-1\">hello\u1234</html>" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{res_body.bytesize} +Content-Type: text/html + +#{res_body} +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = true + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal res_body.force_encoding("ISO-8859-1"), body + assert_equal Encoding::ISO_8859_1, body.encoding + end + + def test_read_body_body_encoding_true_with_utf8_meta_content_charset + res_body = "<meta http-equiv='content-type' content='text/html; charset=UTF-8'>hello\u1234</html>" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{res_body.bytesize} +Content-Type: text/html + +#{res_body} +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = true + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal res_body, body + assert_equal Encoding::UTF_8, body.encoding + end + + def test_read_body_body_encoding_true_with_iso8859_1_meta_content_charset + res_body = "<meta http-equiv='content-type' content='text/html; charset=ISO-8859-1'>hello\u1234</html>" + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: #{res_body.bytesize} +Content-Type: text/html + +#{res_body} +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = true + + body = nil + + res.reading_body io, true do + body = res.read_body + end + + assert_equal res_body.force_encoding("ISO-8859-1"), body + assert_equal Encoding::ISO_8859_1, body.encoding + end + def test_read_body_block io = dummy_io(<<EOS) HTTP/1.1 200 OK @@ -75,6 +311,36 @@ EOS assert_equal 'hello', body end + def test_read_body_block_mod + # http://ci.rvm.jp/results/trunk-rjit-wait@silicon-docker/3019353 + if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? + omit 'too unstable with --jit-wait, and extending read_timeout did not help it' + end + IO.pipe do |r, w| + buf = 'x' * 1024 + buf.freeze + n = 1024 + len = n * buf.size + th = Thread.new do + w.write("HTTP/1.1 200 OK\r\nContent-Length: #{len}\r\n\r\n") + n.times { w.write(buf) } + :ok + end + io = Net::BufferedIO.new(r) + res = Net::HTTPResponse.read_new(io) + nr = 0 + res.reading_body io, true do + # should be allowed to modify the chunk given to them: + res.read_body do |chunk| + nr += chunk.size + chunk.clear + end + end + assert_equal len, nr + assert_equal :ok, th.value + end + end + def test_read_body_content_encoding_deflate io = dummy_io(<<EOS) HTTP/1.1 200 OK @@ -96,9 +362,41 @@ EOS if Net::HTTP::HAVE_ZLIB assert_equal nil, res['content-encoding'] + assert_equal '5', res['content-length'] assert_equal 'hello', body else assert_equal 'deflate', res['content-encoding'] + assert_equal '13', res['content-length'] + 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 '5', res['content-length'] + assert_equal 'hello', body + else + assert_equal 'DEFLATE', res['content-encoding'] + assert_equal '13', res['content-length'] assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body end end @@ -129,9 +427,11 @@ EOS if Net::HTTP::HAVE_ZLIB assert_equal nil, res['content-encoding'] + assert_equal nil, res['content-length'] assert_equal 'hello', body else assert_equal 'deflate', res['content-encoding'] + assert_equal nil, res['content-length'] assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body end end @@ -156,6 +456,7 @@ EOS end assert_equal 'deflate', res['content-encoding'], 'Bug #7831' + assert_equal '13', res['content-length'] assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body, 'Bug #7381' end @@ -179,9 +480,11 @@ EOS if Net::HTTP::HAVE_ZLIB assert_equal nil, res['content-encoding'] + assert_equal nil, res['content-length'] assert_equal 'hello', body else assert_equal 'deflate', res['content-encoding'] + assert_equal nil, res['content-length'] assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15\r\n", body end end @@ -209,6 +512,63 @@ EOS 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 '0', res['content-length'] + assert_equal '', body + else + assert_equal 'deflate', res['content-encoding'] + assert_equal '0', res['content-length'] + 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 nil, res['content-length'] + assert_equal '', body + else + assert_equal 'deflate', res['content-encoding'] + assert_equal nil, res['content-length'] + assert_equal '', body + end + end + def test_read_body_string io = dummy_io(<<EOS) HTTP/1.1 200 OK @@ -229,6 +589,41 @@ EOS assert_equal 'hello', body end + def test_read_body_receiving_no_body + io = dummy_io(<<EOS) +HTTP/1.1 204 OK +Connection: close + +EOS + + res = Net::HTTPResponse.read_new(io) + res.body_encoding = 'utf-8' + + body = 'something to override' + + res.reading_body io, true do + body = res.read_body + end + + assert_equal nil, body + assert_equal nil, res.body + end + + def test_read_body_outside_of_reading_body + io = dummy_io(<<EOS) +HTTP/1.1 200 OK +Connection: close +Content-Length: 0 + +EOS + + res = Net::HTTPResponse.read_new(io) + + assert_raise IOError do + res.read_body + end + end + def test_uri_equals uri = URI 'http://example' @@ -241,7 +636,112 @@ EOS response.uri = uri assert_equal uri, response.uri - refute_same uri, response.uri + assert_not_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_raise 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 + + def test_raises_exception_with_missing_reason + io = dummy_io(<<EOS) +HTTP/1.1 404 +Content-Length: 5 +Connection: close + +hello +EOS + + res = Net::HTTPResponse.read_new(io) + assert_equal(nil, res.message) + assert_raise Net::HTTPClientException do + res.error! + end + end + + def test_read_code_type + res = Net::HTTPUnknownResponse.new('1.0', '???', 'test response') + assert_equal Net::HTTPUnknownResponse, res.code_type + + res = Net::HTTPInformation.new('1.0', '1xx', 'test response') + assert_equal Net::HTTPInformation, res.code_type + + res = Net::HTTPSuccess.new('1.0', '2xx', 'test response') + assert_equal Net::HTTPSuccess, res.code_type + + res = Net::HTTPRedirection.new('1.0', '3xx', 'test response') + assert_equal Net::HTTPRedirection, res.code_type + + res = Net::HTTPClientError.new('1.0', '4xx', 'test response') + assert_equal Net::HTTPClientError, res.code_type + + res = Net::HTTPServerError.new('1.0', '5xx', 'test response') + assert_equal Net::HTTPServerError, res.code_type + end + + def test_inspect_response + res = Net::HTTPUnknownResponse.new('1.0', '???', 'test response') + assert_equal '#<Net::HTTPUnknownResponse ??? test response readbody=false>', res.inspect + + res = Net::HTTPUnknownResponse.new('1.0', '???', 'test response') + socket = Net::BufferedIO.new(StringIO.new('test body')) + res.reading_body(socket, true) {} + assert_equal '#<Net::HTTPUnknownResponse ??? test response readbody=true>', res.inspect end private diff --git a/test/net/http/test_httpresponses.rb b/test/net/http/test_httpresponses.rb index bf7fbeef11..b389e163cf 100644 --- a/test/net/http/test_httpresponses.rb +++ b/test/net/http/test_httpresponses.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: false require 'net/http' require 'test/unit' diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb index b711582b99..f5b21b901f 100644 --- a/test/net/http/test_https.rb +++ b/test/net/http/test_https.rb @@ -1,87 +1,156 @@ +# frozen_string_literal: false require "test/unit" +require_relative "utils" 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 +return unless defined?(OpenSSL::SSL) + 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 - ) + def self.read_fixture(key) + File.read(File.expand_path("../fixtures/#{key}", __dir__)) + end + + HOST = 'localhost' + HOST_IP = '127.0.0.1' + CA_CERT = OpenSSL::X509::Certificate.new(read_fixture("cacert.pem")) + SERVER_KEY = OpenSSL::PKey.read(read_fixture("server.key")) + SERVER_CERT = OpenSSL::X509::Certificate.new(read_fixture("server.crt")) + TEST_STORE = OpenSSL::X509::Store.new.tap {|s| s.add_cert(CA_CERT) } CONFIG = { - 'host' => '127.0.0.1', - 'port' => 0, + 'host' => HOST, 'proxy_host' => nil, 'proxy_port' => nil, 'ssl_enable' => true, - 'ssl_certificate' => cert, - 'ssl_private_key' => key, + 'ssl_certificate' => SERVER_CERT, + 'ssl_private_key' => SERVER_KEY, } def test_get - http = Net::HTTP.new("localhost", config("port")) + http = Net::HTTP.new(HOST, 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.cert_store = TEST_STORE http.request_get("/") {|res| assert_equal($test_net_http_data, res.body) + assert_equal(SERVER_CERT.to_der, http.peer_cert.to_der) } - rescue SystemCallError - skip $! end - def test_post - http = Net::HTTP.new("localhost", config("port")) + def test_get_SNI + http = Net::HTTP.new(HOST, config("port")) + http.ipaddr = config('host') 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 + http.cert_store = TEST_STORE + http.request_get("/") {|res| + assert_equal($test_net_http_data, res.body) + assert_equal(SERVER_CERT.to_der, http.peer_cert.to_der) + } + end + + def test_get_SNI_proxy + TCPServer.open(HOST_IP, 0) {|serv| + _, port, _, _ = serv.addr + client_thread = Thread.new { + proxy = Net::HTTP.Proxy(HOST_IP, port, 'user', 'password') + http = proxy.new("foo.example.org", 8000) + http.ipaddr = "192.0.2.1" + http.use_ssl = true + http.cert_store = TEST_STORE + 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 192.0.2.1: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 + + def test_get_SNI_failure + TestNetHTTPUtils.clean_http_proxy_env do + http = Net::HTTP.new("invalidservername", config("port")) + http.ipaddr = config('host') + http.use_ssl = true + http.cert_store = TEST_STORE + @log_tester = lambda {|_| } + assert_raise(OpenSSL::SSL::SSLError){ http.start } end + end + + def test_post + http = Net::HTTP.new(HOST, config("port")) + http.use_ssl = true + http.cert_store = TEST_STORE data = config('ssl_private_key').to_der - http.request_post("/", data) {|res| + 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 = Net::HTTP.new(HOST, 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 + http.cert_store = TEST_STORE + + if OpenSSL::OPENSSL_LIBRARY_VERSION =~ /LibreSSL (\d+\.\d+)/ && $1.to_f > 3.19 + # LibreSSL 3.2 defaults to TLSv1.3 in server and client, which doesn't currently + # support session resuse. Limiting the version to the TLSv1.2 stack allows + # this test to continue to work on LibreSSL 3.2+. LibreSSL may eventually + # support session reuse, but there are no current plans to do so. + http.ssl_version = :TLSv1_2 end http.start + session_reused = http.instance_variable_get(:@socket).io.session_reused? + assert_false session_reused unless session_reused.nil? # can not detect re-use under JRuby http.get("/") http.finish http.start - http.get("/") - http.finish # three times due to possible bug in OpenSSL 0.9.8 + session_reused = http.instance_variable_get(:@socket).io.session_reused? + assert_true session_reused unless session_reused.nil? # can not detect re-use under JRuby + assert_equal $test_net_http_data, http.get("/").body + http.finish + end + def test_session_reuse_but_expire + http = Net::HTTP.new(HOST, config("port")) + http.use_ssl = true + http.cert_store = TEST_STORE + + http.ssl_timeout = 1 + http.start + http.get("/") + http.finish + sleep 1.25 http.start http.get("/") socket = http.instance_variable_get(:@socket).io + assert_equal false, socket.session_reused?, "NOTE: OpenSSL library version is #{OpenSSL::OPENSSL_LIBRARY_VERSION}" - assert socket.session_reused? - rescue SystemCallError - skip $! + http.finish end if ENV["RUBY_OPENSSL_TEST_ALL"] @@ -96,49 +165,71 @@ class TestNetHTTPS < Test::Unit::TestCase end def test_verify_none - http = Net::HTTP.new("localhost", config("port")) + http = Net::HTTP.new(HOST, 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_skip_hostname_verification + TestNetHTTPUtils.clean_http_proxy_env do + http = Net::HTTP.new('invalidservername', config('port')) + http.ipaddr = config('host') + http.use_ssl = true + http.cert_store = TEST_STORE + http.verify_hostname = false + assert_nothing_raised { http.start } + ensure + http.finish if http&.started? + end + end + + def test_fail_if_verify_hostname_is_true + TestNetHTTPUtils.clean_http_proxy_env do + http = Net::HTTP.new('invalidservername', config('port')) + http.ipaddr = config('host') + http.use_ssl = true + http.cert_store = TEST_STORE + http.verify_hostname = true + @log_tester = lambda { |_| } + assert_raise(OpenSSL::SSL::SSLError) { http.start } + end end def test_certificate_verify_failure - http = Net::HTTP.new("localhost", config("port")) + http = Net::HTTP.new(HOST, config("port")) http.use_ssl = true ex = assert_raise(OpenSSL::SSL::SSLError){ - begin - http.request_get("/") {|res| } - rescue SystemCallError - skip $! - end + http.request_get("/") {|res| } } assert_match(/certificate verify failed/, ex.message) end - def test_identity_verify_failure - http = Net::HTTP.new("127.0.0.1", config("port")) + def test_verify_callback + http = Net::HTTP.new(HOST, 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| } + http.cert_store = TEST_STORE + certs = [] + http.verify_callback = Proc.new {|preverify_ok, store_ctx| + certs << store_ctx.current_cert + preverify_ok + } + http.request_get("/") {|res| + assert_equal($test_net_http_data, res.body) } - assert_match(/hostname \"127.0.0.1\" does not match/, ex.message) + assert_equal(SERVER_CERT.to_der, certs.last.to_der) 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| + TCPServer.open(HOST, 0) {|server| port = server.addr[1] - conn = Net::HTTP.new('localhost', port) + conn = Net::HTTP.new(HOST, port) conn.use_ssl = true conn.read_timeout = 0.01 conn.open_timeout = 0.01 @@ -151,4 +242,84 @@ class TestNetHTTPS < Test::Unit::TestCase assert th.join(10), bug4246 } end -end if defined?(OpenSSL) + + def test_min_version + http = Net::HTTP.new(HOST, config("port")) + http.use_ssl = true + http.min_version = :TLS1 + http.cert_store = TEST_STORE + http.request_get("/") {|res| + assert_equal($test_net_http_data, res.body) + } + end + + def test_max_version + http = Net::HTTP.new(HOST, config("port")) + http.use_ssl = true + http.max_version = :SSL2 + http.cert_store = TEST_STORE + @log_tester = lambda {|_| } + ex = assert_raise(OpenSSL::SSL::SSLError){ + http.request_get("/") {|res| } + } + re_msg = /\ASSL_connect returned=1 errno=0 |SSL_CTX_set_max_proto_version|No appropriate protocol/ + assert_match(re_msg, ex.message) + end + + def test_ractor + assert_ractor(<<~RUBY, require: 'net/https') + expected = #{$test_net_http_data.dump}.b + ret = Ractor.new { + host = #{HOST.dump} + port = #{config('port')} + ca_cert_pem = #{CA_CERT.to_pem.dump} + cert_store = OpenSSL::X509::Store.new.tap { |s| + s.add_cert(OpenSSL::X509::Certificate.new(ca_cert_pem)) + } + Net::HTTP.start(host, port, use_ssl: true, cert_store: cert_store) { |http| + res = http.get('/') + res.body + } + }.value + assert_equal expected, ret + RUBY + end if defined?(Ractor) && Ractor.method_defined?(:value) +end + +class TestNetHTTPSIdentityVerifyFailure < Test::Unit::TestCase + include TestNetHTTPUtils + + def self.read_fixture(key) + File.read(File.expand_path("../fixtures/#{key}", __dir__)) + end + + HOST = 'localhost' + HOST_IP = '127.0.0.1' + CA_CERT = OpenSSL::X509::Certificate.new(read_fixture("cacert.pem")) + SERVER_KEY = OpenSSL::PKey.read(read_fixture("server.key")) + SERVER_CERT = OpenSSL::X509::Certificate.new(read_fixture("server.crt")) + TEST_STORE = OpenSSL::X509::Store.new.tap {|s| s.add_cert(CA_CERT) } + + CONFIG = { + 'host' => HOST_IP, + 'proxy_host' => nil, + 'proxy_port' => nil, + 'ssl_enable' => true, + 'ssl_certificate' => SERVER_CERT, + 'ssl_private_key' => SERVER_KEY, + } + + def test_identity_verify_failure + # the certificate's subject has CN=localhost + http = Net::HTTP.new(HOST_IP, config("port")) + http.use_ssl = true + http.cert_store = TEST_STORE + @log_tester = lambda {|_| } + ex = assert_raise(OpenSSL::SSL::SSLError){ + http.request_get("/") {|res| } + sleep 0.5 + } + re_msg = /certificate verify failed|hostname \"#{HOST_IP}\" does not match/ + assert_match(re_msg, ex.message) + end +end diff --git a/test/net/http/test_https_proxy.rb b/test/net/http/test_https_proxy.rb index 57cabf05e0..237c16e64d 100644 --- a/test/net/http/test_https_proxy.rb +++ b/test/net/http/test_https_proxy.rb @@ -1,21 +1,17 @@ +# frozen_string_literal: false begin require 'net/https' rescue LoadError end require 'test/unit' +return unless defined?(OpenSSL::SSL) + class HTTPSProxyTest < Test::Unit::TestCase def test_https_proxy_authentication - begin - OpenSSL - rescue LoadError - skip 'autoload problem. see [ruby-dev:45021][Bug #5786]' - end - - t = nil TCPServer.open("127.0.0.1", 0) {|serv| _, port, _, _ = serv.addr - t = Thread.new { + 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 @@ -25,19 +21,64 @@ class HTTPSProxyTest < Test::Unit::TestCase rescue EOFError end } - sock = serv.accept - 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]") - sock.close + 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]) } - ensure - t.join if t end -end if defined?(OpenSSL) + + def read_fixture(key) + File.read(File.expand_path("../fixtures/#{key}", __dir__)) + end + + def test_https_proxy_ssl_connection + TCPServer.open("127.0.0.1", 0) {|tcpserver| + ctx = OpenSSL::SSL::SSLContext.new + ctx.key = OpenSSL::PKey.read(read_fixture("server.key")) + ctx.cert = OpenSSL::X509::Certificate.new(read_fixture("server.crt")) + serv = OpenSSL::SSL::SSLServer.new(tcpserver, ctx) + + _, port, _, _ = serv.addr + client_thread = Thread.new { + proxy = Net::HTTP.Proxy("127.0.0.1", port, 'user', 'password', true) + 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-core:96672]") + ensure + sock.close + end + } + assert_join_threads([client_thread, server_thread]) + } + end +end diff --git a/test/net/http/utils.rb b/test/net/http/utils.rb index 02e99d45fb..0b9e440e7c 100644 --- a/test/net/http/utils.rb +++ b/test/net/http/utils.rb @@ -1,12 +1,234 @@ -require 'webrick' -begin - require "webrick/https" -rescue LoadError - # SSL features cannot be tested -end -require 'webrick/httpservlet/abstract' +# frozen_string_literal: false +require 'socket' module TestNetHTTPUtils + + class Forbidden < StandardError; end + + class HTTPServer + def initialize(config, &block) + @config = config + @server = TCPServer.new(@config['host'], 0) + @port = @server.addr[1] + @procs = {} + + if @config['ssl_enable'] + require 'openssl' + context = OpenSSL::SSL::SSLContext.new + context.cert = @config['ssl_certificate'] + context.key = @config['ssl_private_key'] + @ssl_server = OpenSSL::SSL::SSLServer.new(@server, context) + end + + @block = block + end + + def start + @thread = Thread.new do + loop do + socket = (@ssl_server || @server).accept + run(socket) + rescue + ensure + socket&.close + end + ensure + (@ssl_server || @server).close + end + end + + def run(socket) + handle_request(socket) + end + + def shutdown + @thread&.kill + @thread&.join + end + + def mount(path, proc) + @procs[path] = proc + end + + def mount_proc(path, &block) + mount(path, block.to_proc) + end + + def handle_request(socket) + request_line = socket.gets + return if request_line.nil? || request_line.strip.empty? + + method, path, _version = request_line.split + headers = {} + while (line = socket.gets) + break if line.strip.empty? + key, value = line.split(': ', 2) + headers[key] = value.strip + end + + if headers['Expect'] == '100-continue' + socket.write "HTTP/1.1 100 Continue\r\n\r\n" + end + + # Set default Content-Type if not provided + if !headers['Content-Type'] && (method == 'POST' || method == 'PUT' || method == 'PATCH') + headers['Content-Type'] = 'application/octet-stream' + end + + req = Request.new(method, path, headers, socket) + if @procs.key?(req.path) || @procs.key?("#{req.path}/") + proc = @procs[req.path] || @procs["#{req.path}/"] + res = Response.new(socket) + begin + proc.call(req, res) + rescue Forbidden + res.status = 403 + end + res.finish + else + @block.call(method, path, headers, socket) + end + end + + def port + @port + end + + class Request + attr_reader :method, :path, :headers, :query, :body + def initialize(method, path, headers, socket) + @method = method + @path, @query = parse_path_and_query(path) + @headers = headers + @socket = socket + if method == 'POST' && (@path == '/continue' || @headers['Content-Type'].include?('multipart/form-data')) + if @headers['Transfer-Encoding'] == 'chunked' + @body = read_chunked_body + else + @body = read_body + end + @query = @body.split('&').each_with_object({}) do |pair, hash| + key, value = pair.split('=') + hash[key] = value + end if @body && @body.include?('=') + end + end + + def [](key) + @headers[key.downcase] + end + + def []=(key, value) + @headers[key.downcase] = value + end + + def continue + @socket.write "HTTP\/1.1 100 continue\r\n\r\n" + end + + def remote_ip + @socket.peeraddr[3] + end + + def peeraddr + @socket.peeraddr + end + + private + + def parse_path_and_query(path) + path, query_string = path.split('?', 2) + query = {} + if query_string + query_string.split('&').each do |pair| + key, value = pair.split('=', 2) + query[key] = value + end + end + [path, query] + end + + def read_body + content_length = @headers['Content-Length']&.to_i + return unless content_length && content_length > 0 + @socket.read(content_length) + end + + def read_chunked_body + body = "" + while (chunk_size = @socket.gets.strip.to_i(16)) > 0 + body << @socket.read(chunk_size) + @socket.read(2) # read \r\n after each chunk + end + body + end + end + + class Response + attr_accessor :body, :headers, :status, :chunked, :cookies + def initialize(client) + @client = client + @body = "" + @headers = {} + @status = 200 + @chunked = false + @cookies = [] + end + + def [](key) + @headers[key.downcase] + end + + def []=(key, value) + @headers[key.downcase] = value + end + + def write_chunk(chunk) + return unless @chunked + @client.write("#{chunk.bytesize.to_s(16)}\r\n") + @client.write("#{chunk}\r\n") + end + + def finish + @client.write build_response_headers + if @chunked + write_chunk(@body) + @client.write "0\r\n\r\n" + else + @client.write @body + end + end + + private + + def build_response_headers + response = "HTTP/1.1 #{@status} #{status_message(@status)}\r\n" + if @chunked + @headers['Transfer-Encoding'] = 'chunked' + else + @headers['Content-Length'] = @body.bytesize.to_s + end + @headers.each do |key, value| + response << "#{key}: #{value}\r\n" + end + @cookies.each do |cookie| + response << "Set-Cookie: #{cookie}\r\n" + end + response << "\r\n" + response + end + + def status_message(code) + case code + when 200 then 'OK' + when 301 then 'Moved Permanently' + when 403 then 'Forbidden' + else 'Unknown' + end + end + end + end + def start(&block) new().start(&block) end @@ -14,7 +236,7 @@ module TestNetHTTPUtils 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.set_debug_output logfile http end @@ -24,7 +246,7 @@ module TestNetHTTPUtils end def logfile - $DEBUG ? $stderr : NullWriter.new + $stderr if $DEBUG end def setup @@ -32,86 +254,106 @@ module TestNetHTTPUtils end def teardown + sleep 0.5 if @config['ssl_enable'] if @server @server.shutdown - until @server.status == :Stop - sleep 0.1 - end end - # resume global state + @log_tester.call(@log) if @log_tester 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 => config('port'), - :Logger => WEBrick::Log.new(NullWriter.new), - :AccessLog => [], - :ShutdownSocketWithoutClose => true, - :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 = HTTPServer.new(@config) do |method, path, headers, socket| + @log << "DEBUG accept: #{@config['host']}:#{socket.addr[1]}" if @logger_level == :debug + case method + when 'HEAD' + handle_head(path, headers, socket) + when 'GET' + handle_get(path, headers, socket) + when 'POST' + handle_post(path, headers, socket) + when 'PATCH' + handle_patch(path, headers, socket) + else + socket.print "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\n\r\n" + end + end @server.start - @config['port'] = @server[:Port] if @config['port'] == 0 - n_try_max = 5 - begin - TCPSocket.open(config('host'), config('port')).close - rescue Errno::ECONNREFUSED - sleep 0.2 - n_try_max -= 1 - raise 'cannot spawn server; give up' if n_try_max < 0 - retry + @config['port'] = @server.port + end + + def handle_head(path, headers, socket) + if headers['Accept'] != '*/*' + content_type = headers['Accept'] + else + content_type = $test_net_http_data_type end + response = "HTTP/1.1 200 OK\r\nContent-Type: #{content_type}\r\nContent-Length: #{$test_net_http_data.bytesize}" + socket.print(response) + end + + def handle_get(path, headers, socket) + if headers['Accept'] != '*/*' + content_type = headers['Accept'] + else + content_type = $test_net_http_data_type + end + response = "HTTP/1.1 200 OK\r\nContent-Type: #{content_type}\r\nContent-Length: #{$test_net_http_data.bytesize}\r\n\r\n#{$test_net_http_data}" + socket.print(response) + end + + def handle_post(path, headers, socket) + body = socket.read(headers['Content-Length'].to_i) + scheme = headers['X-Request-Scheme'] || 'http' + host = @config['host'] + port = socket.addr[1] + content_type = headers['Content-Type'] || 'application/octet-stream' + charset = parse_content_type(content_type)[1] + path = "#{scheme}://#{host}:#{port}#{path}" + path = path.encode(charset) if charset + response = "HTTP/1.1 200 OK\r\nContent-Type: #{content_type}\r\nContent-Length: #{body.bytesize}\r\nX-request-uri: #{path}\r\n\r\n#{body}" + socket.print(response) + end + + def handle_patch(path, headers, socket) + body = socket.read(headers['Content-Length'].to_i) + content_type = headers['Content-Type'] || 'application/octet-stream' + response = "HTTP/1.1 200 OK\r\nContent-Type: #{content_type}\r\nContent-Length: #{body.bytesize}\r\n\r\n#{body}" + socket.print(response) + end + + def parse_content_type(content_type) + return [nil, nil] unless content_type + type, *params = content_type.split(';').map(&:strip) + charset = params.find { |param| param.start_with?('charset=') } + charset = charset.split('=', 2).last if charset + [type, charset] end $test_net_http = nil - $test_net_http_data = (0...256).to_a.map {|i| i.chr }.join('') * 64 + $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 + def self.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'], + } - # 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 + orig.each_key do |key| + ENV.delete key end - def do_PATCH(req, res) - res['Content-Type'] = req['Content-Type'] - res.body = req.body - res.chunked = @chunked + yield + ensure + orig.each do |key, value| + ENV[key] = value end end - - class NullWriter - def <<(s) end - def puts(*args) end - def print(*args) end - def printf(*args) end - end end diff --git a/test/net/imap/cacert.pem b/test/net/imap/cacert.pem deleted file mode 100644 index bd7e68ac95..0000000000 --- a/test/net/imap/cacert.pem +++ /dev/null @@ -1,60 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 9f:dc:f7:94:98:05:43:4c - 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: Dec 23 10:21:33 2010 GMT - Not After : Jan 1 10:21:33 2014 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 - Public-Key: (1024 bit) - Modulus: - 00:ce:be:2c:9f:47:ba:db:9c:9c:5b:f0:38:3b:f3: - 74:20:37:76:23:9f:84:1c:81:90:b4:3e:00:20:34: - 98:7e:81:69:50:a1:c3:65:96:ea:fa:00:da:8c:cc: - 53:3f:ba:3c:d0:50:7a:5a:b4:6b:ac:d3:2e:18:ca: - 2a:69:b3:6a:6f:38:c2:32:a8:06:b6:0a:30:a9:ee: - 03:38:e9:05:a5:19:23:54:a8:3c:b9:08:ad:2b:72: - 23:df:93:22:c4:46:a8:ea:f1:a6:e9:30:4a:3f:83: - 39:e9:62:8e:8b:a3:5e:67:89:1d:7c:75:de:05:aa: - 58:b1:b7:79:7c:10:80:6d:87 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Key Identifier: - 41:C9:49:37:B1:FA:61:E3:BA:D7:19:3D:D9:DA:8C:B9:82:C9:B4:6A - X509v3 Authority Key Identifier: - keyid:41:C9:49:37:B1:FA:61:E3:BA:D7:19:3D:D9:DA:8C:B9:82:C9:B4:6A - - X509v3 Basic Constraints: - CA:TRUE - Signature Algorithm: sha1WithRSAEncryption - 86:00:33:b9:dd:ff:5f:83:59:5f:c3:29:3c:d7:11:db:10:b3: - d7:d1:70:fb:0a:c6:74:85:c6:ea:e1:15:c4:92:f8:0e:11:cc: - ff:a6:3c:31:c2:2c:66:d8:fe:63:93:9f:b0:97:e6:f5:bc:5c: - 80:68:96:5d:eb:77:b9:23:dd:68:a7:49:03:ff:22:48:55:f1: - 39:7c:20:21:ff:64:52:e1:f6:cf:3c:b3:4d:2c:5c:03:62:ea: - c5:49:99:07:fa:8d:ff:7b:c2:75:0c:ca:24:b5:0b:f5:b7:57: - 3a:10:f0:8a:bb:9a:e8:92:4d:d5:6f:c2:a2:29:36:61:78:a4: - dc:7b ------BEGIN CERTIFICATE----- -MIIC6DCCAlGgAwIBAgIJAJ/c95SYBUNMMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD -VQQGEwJKUDEQMA4GA1UECAwHU2hpbWFuZTEUMBIGA1UEBwwLTWF0ei1lIGNpdHkx -FzAVBgNVBAoMDlJ1YnkgQ29yZSBUZWFtMRUwEwYDVQQDDAxSdWJ5IFRlc3QgQ0Ex -JTAjBgkqhkiG9w0BCQEWFnNlY3VyaXR5QHJ1YnktbGFuZy5vcmcwHhcNMTAxMjIz -MTAyMTMzWhcNMTQwMTAxMTAyMTMzWjCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgM -B1NoaW1hbmUxFDASBgNVBAcMC01hdHotZSBjaXR5MRcwFQYDVQQKDA5SdWJ5IENv -cmUgVGVhbTEVMBMGA1UEAwwMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz -ZWN1cml0eUBydWJ5LWxhbmcub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQDOviyfR7rbnJxb8Dg783QgN3Yjn4QcgZC0PgAgNJh+gWlQocNllur6ANqMzFM/ -ujzQUHpatGus0y4Yyipps2pvOMIyqAa2CjCp7gM46QWlGSNUqDy5CK0rciPfkyLE -Rqjq8abpMEo/gznpYo6Lo15niR18dd4Fqlixt3l8EIBthwIDAQABo1AwTjAdBgNV -HQ4EFgQUQclJN7H6YeO61xk92dqMuYLJtGowHwYDVR0jBBgwFoAUQclJN7H6YeO6 -1xk92dqMuYLJtGowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCGADO5 -3f9fg1lfwyk81xHbELPX0XD7CsZ0hcbq4RXEkvgOEcz/pjwxwixm2P5jk5+wl+b1 -vFyAaJZd63e5I91op0kD/yJIVfE5fCAh/2RS4fbPPLNNLFwDYurFSZkH+o3/e8J1 -DMoktQv1t1c6EPCKu5rokk3Vb8KiKTZheKTcew== ------END CERTIFICATE----- diff --git a/test/net/imap/server.crt b/test/net/imap/server.crt deleted file mode 100644 index d848b26ab0..0000000000 --- a/test/net/imap/server.crt +++ /dev/null @@ -1,61 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - 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: Dec 23 10:23:52 2010 GMT - Not After : Jan 1 10:23:52 2014 GMT - Subject: C=JP, ST=Shimane, O=Ruby Core Team, OU=Ruby Test, CN=localhost - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (1024 bit) - Modulus: - 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 Basic Constraints: - CA:FALSE - Netscape Comment: - OpenSSL Generated Certificate - 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:41:C9:49:37:B1:FA:61:E3:BA:D7:19:3D:D9:DA:8C:B9:82:C9:B4:6A - - Signature Algorithm: sha1WithRSAEncryption - ae:ee:cd:fe:c9:af:48:0b:50:37:ac:6a:f6:68:90:9b:67:df: - 6f:2d:17:c9:3c:a5:da:ad:39:dc:2a:5b:07:88:26:38:19:30: - d6:95:cf:10:69:c7:92:14:83:be:f1:b5:8e:6f:d9:91:51:c5: - 63:ae:1c:89:ac:27:bf:4f:2a:8f:4e:0c:57:42:0a:c9:8e:0c: - f4:f3:02:f7:ea:44:b6:e4:47:05:af:4e:74:e4:87:87:d9:c8: - 76:ed:ab:32:7c:f0:31:34:10:14:bc:a6:37:cd:d7:dc:33:da: - 82:d3:d4:9b:e9:d5:cd:38:cc:fa:81:5f:4e:fd:5f:53:05:5d: - 76:f9 ------BEGIN CERTIFICATE----- -MIIC3jCCAkegAwIBAgIBADANBgkqhkiG9w0BAQUFADCBjDELMAkGA1UEBhMCSlAx -EDAOBgNVBAgMB1NoaW1hbmUxFDASBgNVBAcMC01hdHotZSBjaXR5MRcwFQYDVQQK -DA5SdWJ5IENvcmUgVGVhbTEVMBMGA1UEAwwMUnVieSBUZXN0IENBMSUwIwYJKoZI -hvcNAQkBFhZzZWN1cml0eUBydWJ5LWxhbmcub3JnMB4XDTEwMTIyMzEwMjM1MloX -DTE0MDEwMTEwMjM1MlowYDELMAkGA1UEBhMCSlAxEDAOBgNVBAgMB1NoaW1hbmUx -FzAVBgNVBAoMDlJ1YnkgQ29yZSBUZWFtMRIwEAYDVQQLDAlSdWJ5IFRlc3QxEjAQ -BgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23XQ -Rd6x379xoA6wpea89Byd5SVnZMV7y/Gvxr6aqup+D8wFr+9AaQayyROdfuuiBuLq -fQfHx5nH+9W462N3YisYEsNTWND1x0AMAdEmgjQWCePcZfTcu12lQWDnqXS610y2 -o5zFjImvy+ifBf7q/mQkv+ft4/bQ/Nbr/AaCEPsCAwEAAaN7MHkwCQYDVR0TBAIw -ADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUw -HQYDVR0OBBYEFOh+WKwTewMijZ6vMguEiYCADB7CMB8GA1UdIwQYMBaAFEHJSTex -+mHjutcZPdnajLmCybRqMA0GCSqGSIb3DQEBBQUAA4GBAK7uzf7Jr0gLUDesavZo -kJtn328tF8k8pdqtOdwqWweIJjgZMNaVzxBpx5IUg77xtY5v2ZFRxWOuHImsJ79P -Ko9ODFdCCsmODPTzAvfqRLbkRwWvTnTkh4fZyHbtqzJ88DE0EBS8pjfN19wz2oLT -1Jvp1c04zPqBX079X1MFXXb5 ------END CERTIFICATE----- diff --git a/test/net/imap/server.key b/test/net/imap/server.key deleted file mode 100644 index 7c57546ece..0000000000 --- a/test/net/imap/server.key +++ /dev/null @@ -1,15 +0,0 @@ ------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/test/net/imap/test_imap.rb b/test/net/imap/test_imap.rb deleted file mode 100644 index 9484ab8f6c..0000000000 --- a/test/net/imap/test_imap.rb +++ /dev/null @@ -1,536 +0,0 @@ -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 - end - - def teardown - 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] - Thread.start do - begin - 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 - end - rescue - end - end - begin - begin - imap = Net::IMAP.new(SERVER_ADDR, :port => port) - assert_raise(EOFError) do - imap.logout - end - ensure - imap.disconnect if imap - end - ensure - server.close - end - end - - def test_idle - server = create_tcp_server - port = server.addr[1] - requests = [] - Thread.start do - begin - 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 - end - rescue - end - end - begin - 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 - ensure - server.close - end - end - - def test_exception_during_idle - server = create_tcp_server - port = server.addr[1] - requests = [] - Thread.start do - begin - 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 - end - rescue - end - end - begin - 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 - 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 - ensure - server.close - end - end - - def test_idle_done_not_during_idle - server = create_tcp_server - port = server.addr[1] - requests = [] - Thread.start do - begin - sock = server.accept - begin - sock.print("* OK test server\r\n") - ensure - sock.close - end - rescue - end - end - begin - 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 - ensure - server.close - end - end - - def test_unexpected_bye - server = create_tcp_server - port = server.addr[1] - Thread.start do - begin - 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 - end - rescue - end - end - begin - begin - imap = Net::IMAP.new(SERVER_ADDR, :port => port) - assert_raise(Net::IMAP::ByeResponseError) do - imap.login("user", "password") - end - end - ensure - server.close - end - end - - def test_exception_during_shutdown - server = create_tcp_server - port = server.addr[1] - Thread.start do - begin - 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 - end - rescue - end - end - begin - 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 - ensure - server.close - end - end - - def test_connection_closed_during_idle - server = create_tcp_server - port = server.addr[1] - requests = [] - sock = nil - Thread.start do - begin - sock = server.accept - sock.print("* OK test server\r\n") - requests.push(sock.gets) - sock.print("+ idling\r\n") - rescue - 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 - 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 - ensure - server.close - if sock && !sock.closed? - sock.close - end - end - end - - def test_connection_closed_without_greeting - server = create_tcp_server - port = server.addr[1] - Thread.start do - begin - sock = server.accept - sock.close - rescue - end - end - begin - assert_raise(Net::IMAP::Error) do - Net::IMAP.new(SERVER_ADDR, :port => port) - end - ensure - server.close - 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 - - 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) - 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 - end - end - begin - begin - imap = yield(port) - imap.logout - ensure - imap.disconnect if imap - end - ensure - ssl_server.close - end - end - - def starttls_test - server = create_tcp_server - port = server.addr[1] - Thread.start do - begin - sock = server.accept - 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) - begin - sock.accept - sock.gets - sock.print("* BYE terminating connection\r\n") - sock.print("RUBY0002 OK LOGOUT completed\r\n") - ensure - sock.close - end - rescue - end - end - begin - begin - imap = yield(port) - imap.logout if !imap.disconnected? - ensure - imap.disconnect if imap && !imap.disconnected? - end - ensure - server.close - end - end - - def create_tcp_server - return TCPServer.new(SERVER_ADDR, 0) - end -end diff --git a/test/net/imap/test_imap_response_parser.rb b/test/net/imap/test_imap_response_parser.rb deleted file mode 100644 index c329f4285f..0000000000 --- a/test/net/imap/test_imap_response_parser.rb +++ /dev/null @@ -1,240 +0,0 @@ -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"] - - response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint) -* 1 FETCH (UID 92285 ) -EOF - end - - def test_msg_att_parse_error - parser = Net::IMAP::ResponseParser.new - e = assert_raise(Net::IMAP::ResponseParseError) { - response = 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 -end diff --git a/test/net/pop/test_pop.rb b/test/net/pop/test_pop.rb deleted file mode 100644 index c8aa9a83a8..0000000000 --- a/test/net/pop/test_pop.rb +++ /dev/null @@ -1,132 +0,0 @@ -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 - e = 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] - thread = Thread.start do - sock = server.accept - begin - pop_server_loop(sock, apop) - ensure - sock.close - end - end - 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 - thread.value - 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/test/net/protocol/test_protocol.rb b/test/net/protocol/test_protocol.rb index f6ee4941cf..2f42fa3236 100644 --- a/test/net/protocol/test_protocol.rb +++ b/test/net/protocol/test_protocol.rb @@ -1,19 +1,159 @@ +# frozen_string_literal: true 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("".dup) + 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("") + sio = StringIO.new("".dup) 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("") + sio = StringIO.new("".dup) imio = Net::InternetMessageIO.new(sio) assert_equal(8, imio.write_message("\u3042\r")) assert_equal("\u3042\r\n.\r\n", sio.string) end end + + def create_mockio(capacity: 100, max: nil) + mockio = Object.new + mockio.instance_variable_set(:@str, +'') + mockio.instance_variable_set(:@capacity, capacity) + mockio.instance_variable_set(:@max, max) + def mockio.string; @str; end + def mockio.to_io; self; end + def mockio.wait_writable(sec); sleep sec; false; end + def mockio.write_nonblock(*strs, exception: true) + if @capacity <= @str.bytesize + if exception + raise Net::WaitWritable + else + return :wait_writable + end + end + len = 0 + max = @max ? [@capacity, @str.bytesize + @max].min : @capacity + strs.each do |str| + len1 = @str.bytesize + break if max <= len1 + @str << str.byteslice(0, max - @str.bytesize) + len2 = @str.bytesize + len += len2 - len1 + end + len + end + mockio + end + + def test_readuntil + assert_output("", "") do + sio = StringIO.new("12345".dup) + io = Net::BufferedIO.new(sio) + assert_equal "12345", io.readuntil("5") + end + end + + def test_write0_multibyte + mockio = create_mockio(max: 1) + io = Net::BufferedIO.new(mockio) + assert_equal(3, io.write("\u3042")) + end + + def test_write0_timeout + mockio = create_mockio + io = Net::BufferedIO.new(mockio) + io.write_timeout = 0.1 + assert_raise(Net::WriteTimeout){ io.write("a"*1000) } + end + + def test_write0_success + mockio = create_mockio + io = Net::BufferedIO.new(mockio) + io.write_timeout = 0.1 + len = io.write("a"*10) + assert_equal "a"*10, mockio.string + assert_equal 10, len + end + + def test_write0_success2 + mockio = create_mockio + io = Net::BufferedIO.new(mockio) + io.write_timeout = 0.1 + len = io.write("a"*100) + assert_equal "a"*100, mockio.string + assert_equal 100, len + end + + def test_write0_success_multi1 + mockio = create_mockio + io = Net::BufferedIO.new(mockio) + io.write_timeout = 0.1 + len = io.write("a"*50, "a"*49) + assert_equal "a"*99, mockio.string + assert_equal 99, len + end + + def test_write0_success_multi2 + mockio = create_mockio + io = Net::BufferedIO.new(mockio) + io.write_timeout = 0.1 + len = io.write("a"*50, "a"*50) + assert_equal "a"*100, mockio.string + assert_equal 100, len + end + + def test_write0_timeout_multi1 + mockio = create_mockio + io = Net::BufferedIO.new(mockio) + io.write_timeout = 0.1 + assert_raise(Net::WriteTimeout){ io.write("a"*50,"a"*51) } + end + + def test_write0_timeout_multi2 + mockio = create_mockio + io = Net::BufferedIO.new(mockio) + io.write_timeout = 0.1 + assert_raise(Net::WriteTimeout){ io.write("a"*50,"a"*50,"a") } + end + + class FakeReadPartialIO + def initialize(chunks) + @chunks = chunks.map(&:dup) + end + + def read_nonblock(size, buf = nil, exception: false) + if buf + buf.replace(@chunks.shift) + buf + else + @chunks.shift + end + end + end + + def test_shareable_buffer_leak # https://github.com/ruby/net-protocol/pull/19 + expected_chunks = [ + "aaaaa", + "bbbbb", + ] + fake_io = FakeReadPartialIO.new(expected_chunks) + io = Net::BufferedIO.new(fake_io) + actual_chunks = [] + reader = Net::ReadAdapter.new(-> (chunk) { actual_chunks << chunk }) + io.read(5, reader) + io.read(5, reader) + assert_equal expected_chunks, actual_chunks + end end diff --git a/test/net/smtp/test_response.rb b/test/net/smtp/test_response.rb deleted file mode 100644 index e7011e0fb5..0000000000 --- a/test/net/smtp/test_response.rb +++ /dev/null @@ -1,99 +0,0 @@ -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/test/net/smtp/test_smtp.rb b/test/net/smtp/test_smtp.rb deleted file mode 100644 index 0b8d657559..0000000000 --- a/test/net/smtp/test_smtp.rb +++ /dev/null @@ -1,41 +0,0 @@ -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_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/test/net/smtp/test_ssl_socket.rb b/test/net/smtp/test_ssl_socket.rb deleted file mode 100644 index dc8b03e07a..0000000000 --- a/test/net/smtp/test_ssl_socket.rb +++ /dev/null @@ -1,91 +0,0 @@ -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) |
