summaryrefslogtreecommitdiff
path: root/test/net
diff options
context:
space:
mode:
Diffstat (limited to 'test/net')
-rw-r--r--test/net/fixtures/Makefile15
-rw-r--r--test/net/fixtures/cacert.pem24
-rw-r--r--test/net/fixtures/dhparams.pem29
-rw-r--r--test/net/fixtures/server.crt21
-rw-r--r--test/net/fixtures/server.key27
-rw-r--r--test/net/ftp/test_ftp.rb813
-rw-r--r--test/net/http/test_buffered_io.rb1
-rw-r--r--test/net/http/test_http.rb658
-rw-r--r--test/net/http/test_http_request.rb74
-rw-r--r--test/net/http/test_httpheader.rb135
-rw-r--r--test/net/http/test_httpresponse.rb502
-rw-r--r--test/net/http/test_httpresponses.rb1
-rw-r--r--test/net/http/test_https.rb287
-rw-r--r--test/net/http/test_https_proxy.rb83
-rw-r--r--test/net/http/utils.rb382
-rw-r--r--test/net/imap/cacert.pem60
-rw-r--r--test/net/imap/server.crt61
-rw-r--r--test/net/imap/server.key15
-rw-r--r--test/net/imap/test_imap.rb536
-rw-r--r--test/net/imap/test_imap_response_parser.rb240
-rw-r--r--test/net/pop/test_pop.rb132
-rw-r--r--test/net/protocol/test_protocol.rb144
-rw-r--r--test/net/smtp/test_response.rb99
-rw-r--r--test/net/smtp/test_smtp.rb41
-rw-r--r--test/net/smtp/test_ssl_socket.rb91
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)