summaryrefslogtreecommitdiff
path: root/test/net
diff options
context:
space:
mode:
Diffstat (limited to 'test/net')
-rw-r--r--test/net/fixtures/Makefile6
-rw-r--r--test/net/fixtures/cacert.pem44
-rw-r--r--test/net/fixtures/server.crt99
-rw-r--r--test/net/fixtures/server.key55
-rw-r--r--test/net/ftp/test_buffered_socket.rb48
-rw-r--r--test/net/ftp/test_ftp.rb2474
-rw-r--r--test/net/ftp/test_mlsx_entry.rb98
-rw-r--r--test/net/http/test_http.rb335
-rw-r--r--test/net/http/test_http_request.rb41
-rw-r--r--test/net/http/test_httpheader.rb41
-rw-r--r--test/net/http/test_httpresponse.rb287
-rw-r--r--test/net/http/test_https.rb266
-rw-r--r--test/net/http/test_https_proxy.rb51
-rw-r--r--test/net/http/utils.rb373
-rw-r--r--test/net/imap/test_imap.rb785
-rw-r--r--test/net/imap/test_imap_response_parser.rb324
-rw-r--r--test/net/pop/test_pop.rb166
-rw-r--r--test/net/protocol/test_protocol.rb37
-rw-r--r--test/net/smtp/test_response.rb100
-rw-r--r--test/net/smtp/test_smtp.rb200
-rw-r--r--test/net/smtp/test_ssl_socket.rb97
21 files changed, 1291 insertions, 4636 deletions
diff --git a/test/net/fixtures/Makefile b/test/net/fixtures/Makefile
index b2bc9c7368..88c232e3b6 100644
--- a/test/net/fixtures/Makefile
+++ b/test/net/fixtures/Makefile
@@ -5,11 +5,11 @@ regen_certs:
make server.crt
cacert.pem: server.key
- openssl req -new -x509 -days 1825 -key server.key -out cacert.pem -text -subj "/C=JP/ST=Shimane/L=Matz-e city/O=Ruby Core Team/CN=Ruby Test CA/emailAddress=security@ruby-lang.org"
+ 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 -text -subj "/C=JP/ST=Shimane/O=Ruby Core Team/OU=Ruby Test/CN=localhost"
+ 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 1825 -CA cacert.pem -CAkey server.key -set_serial 00 -in server.csr -req -text -out server.crt
+ 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
index f623bd62ed..24c83f1c65 100644
--- a/test/net/fixtures/cacert.pem
+++ b/test/net/fixtures/cacert.pem
@@ -1,24 +1,24 @@
-----BEGIN CERTIFICATE-----
-MIID7TCCAtWgAwIBAgIJAIltvxrFAuSnMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYD
-VQQGEwJKUDEQMA4GA1UECAwHU2hpbWFuZTEUMBIGA1UEBwwLTWF0ei1lIGNpdHkx
-FzAVBgNVBAoMDlJ1YnkgQ29yZSBUZWFtMRUwEwYDVQQDDAxSdWJ5IFRlc3QgQ0Ex
-JTAjBgkqhkiG9w0BCQEWFnNlY3VyaXR5QHJ1YnktbGFuZy5vcmcwHhcNMTkwMTAy
-MDI1ODI4WhcNMjQwMTAxMDI1ODI4WjCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgM
-B1NoaW1hbmUxFDASBgNVBAcMC01hdHotZSBjaXR5MRcwFQYDVQQKDA5SdWJ5IENv
-cmUgVGVhbTEVMBMGA1UEAwwMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz
-ZWN1cml0eUBydWJ5LWxhbmcub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAznlbjRVhz1NlutHVrhcGnK8W0qug2ujKXv1njSC4U6nJF6py7I9EeehV
-SaKePyv+I9z3K1LnfUHOtUbdwdKC77yN66A6q2aqzu5q09/NSykcZGOIF0GuItYI
-3nvW3IqBddff2ffsyR+9pBjfb5AIPP08WowF9q4s1eGULwZc4w2B8PFhtxYANd7d
-BvGLXFlcufv9tDtzyRi4t7eqxCRJkZQIZNZ6DHHIJrNxejOILfHLarI12yk8VK6L
-2LG4WgGqyeePiRyd1o1MbuiAFYqAwpXNUbRKg5NaZGwBHZk8UZ+uFKt1QMBURO5R
-WFy1c349jbWszTqFyL4Lnbg9HhAowQIDAQABo1AwTjAdBgNVHQ4EFgQU9tEiKdU9
-I9derQyc5nWPnc34nVMwHwYDVR0jBBgwFoAU9tEiKdU9I9derQyc5nWPnc34nVMw
-DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAxj7F/u3C3fgq24N7hGRA
-of7ClFQxGmo/IGT0AISzW3HiVYiFaikKhbO1NwD9aBpD8Zwe62sCqMh8jGV/b0+q
-aOORnWYNy2R6r9FkASAglmdF6xn3bhgGD5ls4pCvcG9FynGnGc24g6MrjFNrBYUS
-2iIZsg36i0IJswo/Dy6HLphCms2BMCD3DeWtfjePUiTmQHJo6HsQIKP/u4N4Fvee
-uMBInei2M4VU74fLXbmKl1F9AEX7JDP3BKSZG19Ch5pnUo4uXM1uNTGsi07P4Y0s
-K44+SKBC0bYEFbDK0eQWMrX3kIhkPxyIWhxdq9/NqPYjShuSEAhA6CSpmRg0pqc+
-mA==
+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/server.crt b/test/net/fixtures/server.crt
index 5ca78a6d14..5d2923795d 100644
--- a/test/net/fixtures/server.crt
+++ b/test/net/fixtures/server.crt
@@ -1,82 +1,21 @@
-Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number: 2 (0x2)
- Signature Algorithm: sha256WithRSAEncryption
- Issuer: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
- Validity
- Not Before: Jan 2 03:27:13 2019 GMT
- Not After : Jan 1 03:27:13 2024 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: (2048 bit)
- Modulus:
- 00:e8:da:9c:01:2e:2b:10:ec:49:cd:5e:07:13:07:
- 9c:70:9e:c6:74:bc:13:c2:e1:6f:c6:82:fd:e3:48:
- e0:2c:a5:68:c7:9e:42:de:60:54:65:e6:6a:14:57:
- 7a:30:d0:cc:b5:b6:d9:c3:d2:df:c9:25:97:54:67:
- cf:f6:be:5e:cb:8b:ee:03:c5:e1:e2:f9:e7:f7:d1:
- 0c:47:f0:b8:da:33:5a:ad:41:ad:e7:b5:a2:7b:b7:
- bf:30:da:60:f8:e3:54:a2:bc:3a:fd:1b:74:d9:dc:
- 74:42:e9:29:be:df:ac:b4:4f:eb:32:f4:06:f1:e1:
- 8c:4b:a8:8b:fb:29:e7:b1:bf:1d:01:ee:73:0f:f9:
- 40:dc:d5:15:79:d9:c6:73:d0:c0:dd:cb:e4:da:19:
- 47:80:c6:14:04:72:fd:9a:7c:8f:11:82:76:49:04:
- 79:cc:f2:5c:31:22:95:13:3e:5d:40:a6:4d:e0:a3:
- 02:26:7d:52:3b:bb:ed:65:a1:0f:ed:6b:b0:3c:d4:
- de:61:15:5e:d3:dd:68:09:9f:4a:57:a5:c2:a9:6d:
- 86:92:c5:f4:a4:d4:b7:13:3b:52:63:24:05:e2:cc:
- e3:8a:3c:d4:35:34:2b:10:bb:58:72:e7:e1:8d:1d:
- 74:8c:61:16:20:3d:d0:1c:4e:8f:6e:fd:fe:64:10:
- 4f:41
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Basic Constraints:
- CA:FALSE
- Netscape Comment:
- OpenSSL Generated Certificate
- X509v3 Subject Key Identifier:
- ED:28:C2:7E:AB:4B:C8:E8:FE:55:6D:66:95:31:1C:2D:60:F9:02:36
- X509v3 Authority Key Identifier:
- keyid:F6:D1:22:29:D5:3D:23:D7:5E:AD:0C:9C:E6:75:8F:9D:CD:F8:9D:53
-
- Signature Algorithm: sha256WithRSAEncryption
- 1d:b8:c5:8b:72:41:20:65:ad:27:6f:15:63:06:26:12:8d:9c:
- ad:ca:f4:db:97:b4:90:cb:ff:35:94:bb:2a:a7:a1:ab:1e:35:
- 2d:a5:3f:c9:24:b0:1a:58:89:75:3e:81:0a:2c:4f:98:f9:51:
- fb:c0:a3:09:d0:0a:9b:e7:a2:b7:c3:60:40:c8:f4:6d:b2:6a:
- 56:12:17:4c:00:24:31:df:9c:60:ae:b1:68:54:a9:e6:b5:4a:
- 04:e6:92:05:86:d9:5a:dc:96:30:a5:58:de:14:99:0f:e5:15:
- 89:3e:9b:eb:80:e3:bd:83:c3:ea:33:35:4b:3e:2f:d3:0d:64:
- 93:67:7f:8d:f5:3f:0c:27:bc:37:5a:cc:d6:47:16:af:5a:62:
- d2:da:51:f8:74:06:6b:24:ad:28:68:08:98:37:7d:ed:0e:ab:
- 1e:82:61:05:d0:ba:75:a0:ab:21:b0:9a:fd:2b:54:86:1d:0d:
- 1f:c2:d4:77:1f:72:26:5e:ad:8a:9f:09:36:6d:44:be:74:c2:
- 5a:3e:ff:5c:9d:75:d6:38:7b:c5:39:f9:44:6e:a1:d1:8e:ff:
- 63:db:c4:bb:c6:91:92:ca:5c:60:9b:1d:eb:0a:de:08:ee:bf:
- da:76:03:65:62:29:8b:f8:7f:c7:86:73:1e:f6:1f:2d:89:69:
- fd:be:bd:6e
-----BEGIN CERTIFICATE-----
-MIID4zCCAsugAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCSlAx
-EDAOBgNVBAgMB1NoaW1hbmUxFDASBgNVBAcMC01hdHotZSBjaXR5MRcwFQYDVQQK
-DA5SdWJ5IENvcmUgVGVhbTEVMBMGA1UEAwwMUnVieSBUZXN0IENBMSUwIwYJKoZI
-hvcNAQkBFhZzZWN1cml0eUBydWJ5LWxhbmcub3JnMB4XDTE5MDEwMjAzMjcxM1oX
-DTI0MDEwMTAzMjcxM1owYDELMAkGA1UEBhMCSlAxEDAOBgNVBAgMB1NoaW1hbmUx
-FzAVBgNVBAoMDlJ1YnkgQ29yZSBUZWFtMRIwEAYDVQQLDAlSdWJ5IFRlc3QxEjAQ
-BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AOjanAEuKxDsSc1eBxMHnHCexnS8E8Lhb8aC/eNI4CylaMeeQt5gVGXmahRXejDQ
-zLW22cPS38kll1Rnz/a+XsuL7gPF4eL55/fRDEfwuNozWq1Bree1onu3vzDaYPjj
-VKK8Ov0bdNncdELpKb7frLRP6zL0BvHhjEuoi/sp57G/HQHucw/5QNzVFXnZxnPQ
-wN3L5NoZR4DGFARy/Zp8jxGCdkkEeczyXDEilRM+XUCmTeCjAiZ9Uju77WWhD+1r
-sDzU3mEVXtPdaAmfSlelwqlthpLF9KTUtxM7UmMkBeLM44o81DU0KxC7WHLn4Y0d
-dIxhFiA90BxOj279/mQQT0ECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhC
-AQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFO0o
-wn6rS8jo/lVtZpUxHC1g+QI2MB8GA1UdIwQYMBaAFPbRIinVPSPXXq0MnOZ1j53N
-+J1TMA0GCSqGSIb3DQEBCwUAA4IBAQAduMWLckEgZa0nbxVjBiYSjZytyvTbl7SQ
-y/81lLsqp6GrHjUtpT/JJLAaWIl1PoEKLE+Y+VH7wKMJ0Aqb56K3w2BAyPRtsmpW
-EhdMACQx35xgrrFoVKnmtUoE5pIFhtla3JYwpVjeFJkP5RWJPpvrgOO9g8PqMzVL
-Pi/TDWSTZ3+N9T8MJ7w3WszWRxavWmLS2lH4dAZrJK0oaAiYN33tDqsegmEF0Lp1
-oKshsJr9K1SGHQ0fwtR3H3ImXq2Knwk2bUS+dMJaPv9cnXXWOHvFOflEbqHRjv9j
-28S7xpGSylxgmx3rCt4I7r/adgNlYimL+H/HhnMe9h8tiWn9vr1u
+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
index 7f2380e71e..6a83d5bcf4 100644
--- a/test/net/fixtures/server.key
+++ b/test/net/fixtures/server.key
@@ -1,28 +1,27 @@
------BEGIN PRIVATE KEY-----
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDo2pwBLisQ7EnN
-XgcTB5xwnsZ0vBPC4W/Ggv3jSOAspWjHnkLeYFRl5moUV3ow0My1ttnD0t/JJZdU
-Z8/2vl7Li+4DxeHi+ef30QxH8LjaM1qtQa3ntaJ7t78w2mD441SivDr9G3TZ3HRC
-6Sm+36y0T+sy9Abx4YxLqIv7Keexvx0B7nMP+UDc1RV52cZz0MDdy+TaGUeAxhQE
-cv2afI8RgnZJBHnM8lwxIpUTPl1Apk3gowImfVI7u+1loQ/ta7A81N5hFV7T3WgJ
-n0pXpcKpbYaSxfSk1LcTO1JjJAXizOOKPNQ1NCsQu1hy5+GNHXSMYRYgPdAcTo9u
-/f5kEE9BAgMBAAECggEBAOHkwhc7DLh8IhTDNSW26oMu5OP2WU1jmiYAigDmf+OQ
-DBgrZj+JQBci8qINQxL8XLukSZn5hvQCLc7Kbyu1/wyEEUFDxSGGwwzclodr9kho
-LX2LDASPZrOSzD2+fPi2wTKmXKuS6Uc44OjQfZkYMNkz9r4Vkm8xGgOD3VipjIYX
-QXlhhdqkXZcNABsihCV52GKkDFSVm8jv95YJc5xhoYCy/3a4/qPdF0aT2R7oYUej
-hKrxVDskyooe8Zg/JTydZNV5GQEDmW01/K3r6XGT26oPi1AqMU1gtv/jkW56CRQQ
-1got8smnqM+AV7Slf9R6DauIPdQJ2S8wsr/o8ISBsOECgYEA9YrqEP2gAYSGFXRt
-liw0WI2Ant8BqXS6yvq1jLo/qWhLw/ph4Di73OQ2mpycVTpgfGr2wFPQR1XJ+0Fd
-U+Ir/C3Q7FK4VIGHK7B0zNvZr5tEjlFfeRezo2JMVw5YWeSagIFcSwK+KqCTH9qc
-pw/Eb8nB/4XNcpTZu7Fg0Wc+ooUCgYEA8sVaicn1Wxkpb45a4qfrA6wOr5xdJ4cC
-A5qs7vjX2OdPIQOmoQhdI7bCWFXZzF33wA4YCws6j5wRaySLIJqdms8Gl9QnODy1
-ZlA5gwKToBC/jqPmWAXSKb8EH7cHilaxU9OKnQ7CfwlGLHqjMtjrhR7KHlt3CVRs
-oRmvsjZVXI0CgYAmPedslAO6mMhFSSfULrhMXmV82OCqYrrA6EEkVNGbcdnzAOkD
-gfKIWabDd8bFY10po4Mguy0CHzNhBXIioWQWV5BlbhC1YKMLw+S9DzSdLAKGY9gJ
-xQ4+UQ3wtRQ/k+IYR413RUsW2oFvgZ3KSyNeAb9MK6uuv84VdG/OzVSs/QKBgQDn
-kap//l2EbObiWyaERunckdVcW0lcN+KK75J/TGwPoOwQsLvTpPe65kxRGGrtDsEQ
-uCDk/+v3KkZPLgdrrTAih9FhJ+PVN8tMcb+6IM4SA4fFFr/UPJEwct0LJ3oQ0grJ
-y+HPWFHb/Uurh7t99/4H98uR02sjQh1wOeEmm78mzQKBgQDm+LzGH0se6CXQ6cdZ
-g1JRZeXkDEsrW3hfAsW62xJQmXcWxBoblP9OamMY+A06rM5og3JbDk5Zm6JsOaA8
-wS2gw4ilp46jors4eQey8ux7kB9LzdBoDBBElnsbjLO8oBNZlVcYXg+6BOl/CUi7
-2whRF0FEjKA8ehrNhAq+VFfFNw==
------END PRIVATE KEY-----
+-----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_buffered_socket.rb b/test/net/ftp/test_buffered_socket.rb
deleted file mode 100644
index 875c53f4e0..0000000000
--- a/test/net/ftp/test_buffered_socket.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# frozen_string_literal: true
-
-require "net/ftp"
-require "test/unit"
-require "ostruct"
-require "stringio"
-
-class BufferedSocketTest < Test::Unit::TestCase
- def test_gets_empty
- sock = create_buffered_socket("")
- assert_equal(nil, sock.gets)
- end
-
- def test_gets_one_line
- sock = create_buffered_socket("foo\n")
- assert_equal("foo\n", sock.gets)
- end
-
- def test_gets_one_line_without_term
- sock = create_buffered_socket("foo")
- assert_equal("foo", sock.gets)
- end
-
- def test_gets_two_lines
- sock = create_buffered_socket("foo\nbar\n")
- assert_equal("foo\n", sock.gets)
- assert_equal("bar\n", sock.gets)
- end
-
- def test_gets_two_lines_without_term
- sock = create_buffered_socket("foo\nbar")
- assert_equal("foo\n", sock.gets)
- assert_equal("bar", sock.gets)
- end
-
- def test_read_nil
- sock = create_buffered_socket("foo\nbar")
- assert_equal("foo\nbar", sock.read)
- assert_equal("", sock.read)
- end
-
- private
-
- def create_buffered_socket(s)
- io = StringIO.new(s)
- return Net::FTP::BufferedSocket.new(io)
- end
-end
diff --git a/test/net/ftp/test_ftp.rb b/test/net/ftp/test_ftp.rb
deleted file mode 100644
index 39f1220dbb..0000000000
--- a/test/net/ftp/test_ftp.rb
+++ /dev/null
@@ -1,2474 +0,0 @@
-# frozen_string_literal: true
-
-require "net/ftp"
-require "test/unit"
-require "ostruct"
-require "stringio"
-require "tempfile"
-require "tmpdir"
-
-class FTPTest < Test::Unit::TestCase
- SERVER_NAME = "localhost"
- SERVER_ADDR =
- begin
- Addrinfo.getaddrinfo(SERVER_NAME, 0, nil, :STREAM)[0].ip_address
- rescue SocketError
- "127.0.0.1"
- end
- CA_FILE = File.expand_path("../fixtures/cacert.pem", __dir__)
- SERVER_KEY = File.expand_path("../fixtures/server.key", __dir__)
- SERVER_CERT = File.expand_path("../fixtures/server.crt", __dir__)
-
- def setup
- @thread = nil
- @default_passive = Net::FTP.default_passive
- Net::FTP.default_passive = false
- end
-
- def teardown
- Net::FTP.default_passive = @default_passive
- 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_closed_when_not_connected
- ftp = Net::FTP.new
- assert_equal(true, ftp.closed?)
- assert_nothing_raised(Net::FTPConnectionError) do
- ftp.close
- 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.remote_address = OpenStruct.new
- sock.remote_address.ip_address = "1080:0000:0000:0000:0008:0800:200c:417a"
- ftp.instance_variable_set(:@bare_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
-
- def test_implicit_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("332 Need account for login.\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(SERVER_ADDR,
- port: server.port,
- username: "foo",
- password: "bar",
- account: "baz")
- assert_equal("USER foo\r\n", commands.shift)
- assert_equal("PASS bar\r\n", commands.shift)
- assert_equal("ACCT baz\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_s_open
- 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
- Net::FTP.open(SERVER_ADDR, port: server.port, username: "anonymous") do
- end
- assert_equal("USER anonymous\r\n", commands.shift)
- assert_equal("PASS anonymous@\r\n", commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- server.close
- end
- end
-
- def test_s_new_timeout_options
- ftp = Net::FTP.new
- assert_equal(nil, ftp.open_timeout)
- assert_equal(60, ftp.read_timeout)
-
- ftp = Net::FTP.new(nil, open_timeout: 123, read_timeout: 234)
- assert_equal(123, ftp.open_timeout)
- assert_equal(234, ftp.read_timeout)
- 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(2.0) # Net::ReadTimeout
- 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.4
- 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 = 1.0
- 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(1.0, 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)
- host, port = process_port_or_eprt(sock, line)
- 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(/\A(PORT|EPRT) /, 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)
- host, port = process_port_or_eprt(sock, line)
- 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 = 1.0
- 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(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("LIST\r\n", commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_list_fail
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("331 Please specify the password.\r\n")
- commands.push(sock.gets)
- sock.print("230 Login successful.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to Binary mode.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to ASCII mode.\r\n")
- line = sock.gets
- commands.push(line)
- host, port = process_port_or_eprt(sock, line)
- 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")
- [host, port]
- }
- 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_raise(Net::FTPPermError){ ftp.list }
- assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("LIST\r\n", commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_open_data_port_fail_no_leak
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("331 Please specify the password.\r\n")
- commands.push(sock.gets)
- sock.print("230 Login successful.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to Binary mode.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to ASCII mode.\r\n")
- line = sock.gets
- commands.push(line)
- sock.print("421 Service not available, closing control connection.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to Binary mode.\r\n")
- }
- begin
- begin
- ftp = Net::FTP.new
- ftp.connect(SERVER_ADDR, server.port)
- ftp.login
- assert_match(/\AUSER /, commands.shift)
- assert_match(/\APASS /, commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- assert_raise(Net::FTPTempError){ ftp.list }
- assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, 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)
- host, port = process_port_or_eprt(sock, line)
- 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(1.0)
- 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.5
- 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 = String.new
- 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(/\A(PORT|EPRT) /, 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)
- host, port = process_port_or_eprt(sock, line)
- 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.2)
- 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 = 1.0
- 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 = String.new
- ftp.retrbinary("RETR foo", 1024) do |s|
- buf << s
- end
- assert_equal(binary_data.bytesize, buf.bytesize)
- assert_equal(binary_data, buf)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("RETR foo\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_retrbinary_fail
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("331 Please specify the password.\r\n")
- commands.push(sock.gets)
- sock.print("230 Login successful.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to Binary mode.\r\n")
- line = sock.gets
- commands.push(line)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("550 Requested action not taken.\r\n")
- [host, port]
- }
- 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_raise(Net::FTPPermError){ ftp.retrbinary("RETR foo", 1024) }
- assert_match(/\A(PORT|EPRT) /, 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_getbinaryfile
- 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)
- host, port = process_port_or_eprt(sock, line)
- 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|
- 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.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.getbinaryfile("foo", nil)
- assert_equal(binary_data, buf)
- assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_match(/\A(PORT|EPRT) /, 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_getbinaryfile_empty
- commands = []
- binary_data = ""
- 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)
- host, port = process_port_or_eprt(sock, line)
- 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)
- conn.shutdown(Socket::SHUT_WR)
- conn.read
- conn.close
- sock.print("226 Transfer complete.\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)
- buf = ftp.getbinaryfile("foo", nil)
- assert_equal(binary_data, buf)
- assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_match(/\A(PORT|EPRT) /, 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_getbinaryfile_with_filename_and_block
- 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)
- host, port = process_port_or_eprt(sock, line)
- 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|
- 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.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)
- Tempfile.create("foo", external_encoding: "ASCII-8BIT") do |f|
- f.binmode
- buf = String.new
- res = ftp.getbinaryfile("foo", f.path) { |s|
- buf << s
- }
- assert_equal(nil, res)
- assert_equal(binary_data, buf)
- assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_equal(binary_data, f.read)
- end
- assert_match(/\A(PORT|EPRT) /, 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)
- host, port = process_port_or_eprt(sock, line)
- 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.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(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("STOR foo\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_storbinary_fail
- commands = []
- binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("331 Please specify the password.\r\n")
- commands.push(sock.gets)
- sock.print("230 Login successful.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to Binary mode.\r\n")
- line = sock.gets
- commands.push(line)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("452 Requested file action aborted.\r\n")
- [host, port]
- }
- 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_raise(Net::FTPTempError){ ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024) }
- assert_match(/\A(PORT|EPRT) /, 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_retrlines
- commands = []
- text_data = <<EOF.gsub(/\n/, "\r\n")
-foo
-bar
-baz
-EOF
- 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)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("150 Opening TEXT mode data connection for foo (#{text_data.size} bytes)\r\n")
- conn = TCPSocket.new(host, port)
- text_data.each_line do |l|
- conn.print(l)
- end
- conn.shutdown(Socket::SHUT_WR)
- conn.read
- conn.close
- sock.print("226 Transfer complete.\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)
- buf = String.new
- ftp.retrlines("RETR foo") do |line|
- buf << line + "\r\n"
- end
- assert_equal(text_data.bytesize, buf.bytesize)
- assert_equal(text_data, buf)
- assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("RETR foo\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_gettextfile
- commands = []
- text_data = <<EOF.gsub(/\n/, "\r\n")
-foo
-bar
-baz
-EOF
- 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)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("150 Opening TEXT mode data connection for foo (#{text_data.size} bytes)\r\n")
- conn = TCPSocket.new(host, port)
- text_data.each_line do |l|
- conn.print(l)
- end
- conn.shutdown(Socket::SHUT_WR)
- conn.read
- conn.close
- sock.print("226 Transfer complete.\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)
- buf = ftp.gettextfile("foo", nil)
- assert_equal(text_data.gsub(/\r\n/, "\n"), buf)
- assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("RETR foo\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_gettextfile_with_filename_and_block
- commands = []
- text_data = <<EOF.gsub(/\n/, "\r\n")
-foo
-bar
-baz
-EOF
- 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)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("150 Opening TEXT mode data connection for foo (#{text_data.size} bytes)\r\n")
- conn = TCPSocket.new(host, port)
- text_data.each_line do |l|
- conn.print(l)
- end
- conn.shutdown(Socket::SHUT_WR)
- conn.read
- conn.close
- sock.print("226 Transfer complete.\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)
- Tempfile.create("foo", external_encoding: "ascii-8bit") do |f|
- buf = String.new
- res = ftp.gettextfile("foo", f.path) { |s|
- buf << s << "\n"
- }
- assert_equal(nil, res)
- assert_equal(text_data.gsub(/\r\n/, "\n"), buf)
- assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_equal(buf, f.read)
- end
- assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("RETR foo\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_getbinaryfile_in_list
- commands = []
- binary_data = (0..0xff).map {|i| i.chr}.join
- 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.bin"
- ]
- 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)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("150 Here comes the directory listing.\r\n")
- conn = TCPSocket.new(host, port)
- list_lines.each_with_index do |l, i|
- 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")
- line = sock.gets
- commands.push(line)
- host, port = process_port_or_eprt(sock, line)
- 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)
- conn.print(binary_data)
- conn.close
- sock.print("226 Transfer complete.\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)
- ftp.list do |line|
- file = line.slice(/(\S*\.bin)\z/)
- if file
- data = ftp.getbinaryfile(file, nil)
- assert_equal(binary_data, data)
- end
- end
- assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("LIST\r\n", commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("RETR baz.bin\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.gets)
- sock.print("225 No transfer to ABOR.\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)
- ftp.abort
- assert_equal("ABOR\r\n", 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.gets)
- sock.print("211 End of status\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)
- ftp.status
- assert_equal("STAT\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_status_path
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("331 Please specify the password.\r\n")
- commands.push(sock.gets)
- sock.print("230 Login successful.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to Binary mode.\r\n")
- commands.push(sock.gets)
- sock.print("213 End of status\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)
- ftp.status "/"
- assert_equal("STAT /\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_pathnames
- require 'pathname'
-
- 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")
- commands.push(sock.gets)
- sock.print("257 'foo' directory created.\r\n")
- commands.push(sock.gets)
- sock.print("250 CWD command successful.\r\n")
- commands.push(sock.gets)
- sock.print("250 CWD command successful.\r\n")
- commands.push(sock.gets)
- sock.print("250 RMD command successful.\r\n")
- commands.push(sock.gets)
- sock.print("213 test.txt Fri, 11 Jan 2013 11:20:41 -0500.\r\n")
- commands.push(sock.gets)
- sock.print("213 test.txt 16.\r\n")
- commands.push(sock.gets)
- sock.print("350 File exists, ready for destination name\r\n")
- commands.push(sock.gets)
- sock.print("250 RNTO command successful.\r\n")
- commands.push(sock.gets)
- sock.print("250 DELE command successful.\r\n")
- }
-
- begin
- begin
- dir = Pathname.new("foo")
- file = Pathname.new("test.txt")
- file2 = Pathname.new("test2.txt")
- ftp = Net::FTP.new
- ftp.connect(SERVER_ADDR, server.port)
- ftp.login
- ftp.mkdir(dir)
- ftp.chdir(dir)
- ftp.chdir("..")
- ftp.rmdir(dir)
- ftp.mdtm(file)
- ftp.size(file)
- ftp.rename(file, file2)
- ftp.delete(file)
-
- # TODO: These commented tests below expose the error but don't test anything:
- # TypeError: no implicit conversion of Pathname into String
- # ftp.nlst(dir)
- # ftp.putbinaryfile(Pathname.new("/etc/hosts"), file2)
- # ftp.puttextfile(Pathname.new("/etc/hosts"), file2)
- # ftp.gettextfile(Pathname.new("/etc/hosts"), file2)
- # ftp.getbinaryfile(Pathname.new("/etc/hosts"), file2)
- # ftp.list(dir, dir, dir)
-
- assert_match(/\AUSER /, commands.shift)
- assert_match(/\APASS /, commands.shift)
- assert_match(/\ATYPE /, commands.shift)
- assert_match(/\AMKD /, commands.shift)
- assert_match(/\ACWD /, commands.shift)
- assert_match(/\ACDUP/, commands.shift)
- assert_match(/\ARMD /, commands.shift)
- assert_match(/\AMDTM /, commands.shift)
- assert_match(/\ASIZE /, commands.shift)
- assert_match(/\ARNFR /, commands.shift)
- assert_match(/\ARNTO /, commands.shift)
- assert_match(/\ADELE /, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_getmultiline
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- sock.print("123- foo\r\n")
- sock.print("bar\r\n")
- sock.print(" 123 baz\r\n")
- sock.print("123 quux\r\n")
- sock.print("123 foo\r\n")
- sock.print("foo\r\n")
- sock.print("\r\n")
- }
- begin
- begin
- ftp = Net::FTP.new
- ftp.connect(SERVER_ADDR, server.port)
- assert_equal("123- foo\nbar\n 123 baz\n123 quux\n",
- ftp.send(:getmultiline))
- assert_equal("123 foo\n", ftp.send(:getmultiline))
- assert_equal("foo\n", ftp.send(:getmultiline))
- assert_equal("\n", ftp.send(:getmultiline))
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_size
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("213 12345\r\n")
- }
- begin
- begin
- ftp = Net::FTP.new
- ftp.connect(SERVER_ADDR, server.port)
- assert_equal(12345, ftp.size("foo.txt"))
- assert_match("SIZE foo.txt\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_mdtm
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("213 20150910161739\r\n")
- }
- begin
- begin
- ftp = Net::FTP.new
- ftp.connect(SERVER_ADDR, server.port)
- assert_equal("20150910161739", ftp.mdtm("foo.txt"))
- assert_match("MDTM foo.txt\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_mtime
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("213 20150910161739\r\n")
- commands.push(sock.gets)
- sock.print("213 20150910161739\r\n")
- commands.push(sock.gets)
- sock.print("213 20150910161739.123456\r\n")
- commands.push(sock.gets)
- sock.print("213 20150910161739.123\r\n")
- commands.push(sock.gets)
- sock.print("213 20150910161739.123456789\r\n")
- commands.push(sock.gets)
- sock.print("213 2015091016173\r\n")
- }
- begin
- begin
- ftp = Net::FTP.new
- ftp.connect(SERVER_ADDR, server.port)
- assert_equal(Time.utc(2015, 9, 10, 16, 17, 39), ftp.mtime("foo.txt"))
- assert_equal(Time.local(2015, 9, 10, 16, 17, 39),
- ftp.mtime("foo.txt", true))
- assert_equal(Time.utc(2015, 9, 10, 16, 17, 39, 123456),
- ftp.mtime("bar.txt"))
- assert_equal(Time.utc(2015, 9, 10, 16, 17, 39, 123000),
- ftp.mtime("bar.txt"))
- assert_equal(Time.utc(2015, 9, 10, 16, 17, 39,
- Rational(123456789, 1000)),
- ftp.mtime("bar.txt"))
- assert_raise(Net::FTPProtoError) do
- ftp.mtime("quux.txt")
- end
- assert_match("MDTM foo.txt\r\n", commands.shift)
- assert_match("MDTM foo.txt\r\n", commands.shift)
- assert_match("MDTM bar.txt\r\n", commands.shift)
- assert_match("MDTM bar.txt\r\n", commands.shift)
- assert_match("MDTM bar.txt\r\n", commands.shift)
- assert_match("MDTM quux.txt\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_system
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("215 UNIX Type: L8\r\n")
- }
- begin
- begin
- ftp = Net::FTP.new
- ftp.connect(SERVER_ADDR, server.port)
- assert_equal("UNIX Type: L8", ftp.system)
- assert_match("SYST\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_mlst
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("250- Listing foo\r\n")
- sock.print(" Type=file;Unique=FC00U1E554A;Size=1234567;Modify=20131220035929;Perm=r;Unix.mode=0644;Unix.owner=122;Unix.group=0;Unix.ctime=20131220120140;Unix.atime=20131220131139; /foo\r\n")
- sock.print("250 End\r\n")
- commands.push(sock.gets)
- sock.print("250 Malformed response\r\n")
- commands.push(sock.gets)
- sock.print("250- Listing foo\r\n")
- sock.print("\r\n")
- sock.print("250 End\r\n")
- commands.push(sock.gets)
- sock.print("250- Listing foo\r\n")
- sock.print(" abc /foo\r\n")
- sock.print("250 End\r\n")
- }
- begin
- begin
- ftp = Net::FTP.new
- ftp.connect(SERVER_ADDR, server.port)
- entry = ftp.mlst("foo")
- assert_equal("/foo", entry.pathname)
- assert_equal("file", entry.facts["type"])
- assert_equal("FC00U1E554A", entry.facts["unique"])
- assert_equal(1234567, entry.facts["size"])
- assert_equal("r", entry.facts["perm"])
- assert_equal(0644, entry.facts["unix.mode"])
- assert_equal(122, entry.facts["unix.owner"])
- assert_equal(0, entry.facts["unix.group"])
- modify = entry.facts["modify"]
- assert_equal(2013, modify.year)
- assert_equal(12, modify.month)
- assert_equal(20, modify.day)
- assert_equal(3, modify.hour)
- assert_equal(59, modify.min)
- assert_equal(29, modify.sec)
- assert_equal(true, modify.utc?)
- ctime = entry.facts["unix.ctime"]
- assert_equal(12, ctime.hour)
- assert_equal(1, ctime.min)
- assert_equal(40, ctime.sec)
- atime = entry.facts["unix.atime"]
- assert_equal(13, atime.hour)
- assert_equal(11, atime.min)
- assert_equal(39, atime.sec)
- assert_match("MLST foo\r\n", commands.shift)
- assert_raise(Net::FTPProtoError) do
- ftp.mlst("foo")
- end
- assert_match("MLST foo\r\n", commands.shift)
- assert_raise(Net::FTPProtoError) do
- ftp.mlst("foo")
- end
- assert_match("MLST foo\r\n", commands.shift)
- entry = ftp.mlst("foo")
- assert_equal("/foo", entry.pathname)
- assert_match("MLST foo\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_mlsd
- commands = []
- entry_lines = [
- "Type=file;Unique=FC00U1E554A;Size=1234567;Modify=20131220035929.123456;Perm=r; foo bar",
- "Type=cdir;Unique=FC00U1E554B;Modify=20131220035929;Perm=flcdmpe; .",
- "Type=pdir;Unique=FC00U1E554C;Modify=20131220035929;Perm=flcdmpe; ..",
- ]
- 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)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("150 Here comes the directory listing.\r\n")
- begin
- conn = TCPSocket.new(host, port)
- entry_lines.each do |l|
- conn.print(l, "\r\n")
- end
- rescue Errno::EPIPE
- ensure
- assert_nil($!)
- conn.close
- end
- 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.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)
- entries = ftp.mlsd("/")
- assert_equal(3, entries.size)
- assert_equal("foo bar", entries[0].pathname)
- assert_equal(".", entries[1].pathname)
- assert_equal("..", entries[2].pathname)
- assert_equal("file", entries[0].facts["type"])
- assert_equal("cdir", entries[1].facts["type"])
- assert_equal("pdir", entries[2].facts["type"])
- assert_equal("flcdmpe", entries[1].facts["perm"])
- modify = entries[0].facts["modify"]
- assert_equal(2013, modify.year)
- assert_equal(12, modify.month)
- assert_equal(20, modify.day)
- assert_equal(3, modify.hour)
- assert_equal(59, modify.min)
- assert_equal(29, modify.sec)
- assert_equal(123456, modify.usec)
- assert_equal(true, modify.utc?)
- assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_match("MLSD /\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_parse257
- ftp = Net::FTP.new
- assert_equal('/foo/bar',
- ftp.send(:parse257, '257 "/foo/bar" directory created'))
- assert_equal('/foo/bar"baz',
- ftp.send(:parse257, '257 "/foo/bar""baz" directory created'))
- assert_equal('/foo/x"y"z',
- ftp.send(:parse257, '257 "/foo/x""y""z" directory created'))
- assert_equal('/foo/bar',
- ftp.send(:parse257, '257 "/foo/bar" "comment"'))
- assert_equal('',
- ftp.send(:parse257, '257 "" directory created'))
- assert_equal('',
- ftp.send(:parse257, '257 directory created'))
- assert_raise(Net::FTPReplyError) do
- ftp.send(:parse257, "500 Syntax error")
- end
- end
-
- def test_putline_reject_crlf
- ftp = Net::FTP.new
- assert_raise(ArgumentError) do
- ftp.send(:putline, "\r")
- end
- assert_raise(ArgumentError) do
- ftp.send(:putline, "\n")
- end
- end
-
- if defined?(OpenSSL::SSL)
- def test_tls_unknown_ca
- assert_raise(OpenSSL::SSL::SSLError) do
- tls_test do |port|
- begin
- Net::FTP.new(SERVER_NAME,
- :port => port,
- :ssl => true)
- rescue SystemCallError
- skip $!
- end
- end
- end
- end
-
- def test_tls_with_ca_file
- assert_nothing_raised do
- tls_test do |port|
- begin
- Net::FTP.new(SERVER_NAME,
- :port => port,
- :ssl => { :ca_file => CA_FILE })
- rescue SystemCallError
- skip $!
- end
- end
- end
- end
-
- def test_tls_verify_none
- assert_nothing_raised do
- tls_test do |port|
- Net::FTP.new(SERVER_ADDR,
- :port => port,
- :ssl => { :verify_mode => OpenSSL::SSL::VERIFY_NONE })
- end
- end
- end
-
- def test_tls_post_connection_check
- assert_raise(OpenSSL::SSL::SSLError) do
- tls_test do |port|
- # SERVER_ADDR is different from the hostname in the certificate,
- # so the following code should raise a SSLError.
- Net::FTP.new(SERVER_ADDR,
- :port => port,
- :ssl => { :ca_file => CA_FILE })
- end
- end
- end
-
- def test_active_private_data_connection
- server = TCPServer.new(SERVER_ADDR, 0)
- port = server.addr[1]
- commands = []
- session_reused_for_data_connection = nil
- binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
- @thread = Thread.start do
- sock = server.accept
- begin
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("234 AUTH success.\r\n")
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ca_file = CA_FILE
- ctx.key = File.open(SERVER_KEY) { |f|
- OpenSSL::PKey::RSA.new(f)
- }
- ctx.cert = File.open(SERVER_CERT) { |f|
- OpenSSL::X509::Certificate.new(f)
- }
- sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- sock.sync_close = true
- begin
- sock.accept
- commands.push(sock.gets)
- sock.print("200 PSBZ success.\r\n")
- commands.push(sock.gets)
- sock.print("200 PROT success.\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)
- host, port = process_port_or_eprt(sock, line)
- 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)
- conn = OpenSSL::SSL::SSLSocket.new(conn, ctx)
- conn.sync_close = true
- conn.accept
- session_reused_for_data_connection = conn.session_reused?
- binary_data.scan(/.{1,1024}/nm) do |s|
- conn.print(s)
- end
- conn.close
- sock.print("226 Transfer complete.\r\n")
- rescue OpenSSL::SSL::SSLError
- end
- ensure
- sock.close
- server.close
- end
- end
- ftp = Net::FTP.new(SERVER_NAME,
- port: port,
- ssl: { ca_file: CA_FILE },
- passive: false)
- begin
- assert_equal("AUTH TLS\r\n", commands.shift)
- assert_equal("PBSZ 0\r\n", commands.shift)
- assert_equal("PROT P\r\n", commands.shift)
- ftp.login
- assert_match(/\AUSER /, commands.shift)
- assert_match(/\APASS /, commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- buf = ftp.getbinaryfile("foo", nil)
- assert_equal(binary_data, buf)
- assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("RETR foo\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
- # See https://github.com/openssl/openssl/pull/5967 for details.
- if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h/
- assert_equal(true, session_reused_for_data_connection)
- end
- ensure
- ftp.close
- end
- end
-
- def test_passive_private_data_connection
- server = TCPServer.new(SERVER_ADDR, 0)
- port = server.addr[1]
- commands = []
- session_reused_for_data_connection = nil
- binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
- @thread = Thread.start do
- sock = server.accept
- begin
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("234 AUTH success.\r\n")
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ca_file = CA_FILE
- ctx.key = File.open(SERVER_KEY) { |f|
- OpenSSL::PKey::RSA.new(f)
- }
- ctx.cert = File.open(SERVER_CERT) { |f|
- OpenSSL::X509::Certificate.new(f)
- }
- sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- sock.sync_close = true
- begin
- sock.accept
- commands.push(sock.gets)
- sock.print("200 PSBZ success.\r\n")
- commands.push(sock.gets)
- sock.print("200 PROT success.\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)
- data_server = create_data_connection_server(sock)
- commands.push(sock.gets)
- sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
- conn = data_server.accept
- conn = OpenSSL::SSL::SSLSocket.new(conn, ctx)
- conn.sync_close = true
- conn.accept
- session_reused_for_data_connection = conn.session_reused?
- binary_data.scan(/.{1,1024}/nm) do |s|
- conn.print(s)
- end
- conn.close
- data_server.close
- sock.print("226 Transfer complete.\r\n")
- rescue OpenSSL::SSL::SSLError
- end
- ensure
- sock.close
- server.close
- end
- end
- ftp = Net::FTP.new(SERVER_NAME,
- port: port,
- ssl: { ca_file: CA_FILE },
- passive: true)
- begin
- assert_equal("AUTH TLS\r\n", commands.shift)
- assert_equal("PBSZ 0\r\n", commands.shift)
- assert_equal("PROT P\r\n", commands.shift)
- ftp.login
- assert_match(/\AUSER /, commands.shift)
- assert_match(/\APASS /, commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- buf = ftp.getbinaryfile("foo", nil)
- assert_equal(binary_data, buf)
- assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_match(/\A(PASV|EPSV)\r\n/, commands.shift)
- assert_equal("RETR foo\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
- if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h/
- assert_equal(true, session_reused_for_data_connection)
- end
- ensure
- ftp.close
- end
- end
-
- def test_active_clear_data_connection
- server = TCPServer.new(SERVER_ADDR, 0)
- port = server.addr[1]
- commands = []
- binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
- @thread = Thread.start do
- sock = server.accept
- begin
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("234 AUTH success.\r\n")
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ca_file = CA_FILE
- ctx.key = File.open(SERVER_KEY) { |f|
- OpenSSL::PKey::RSA.new(f)
- }
- ctx.cert = File.open(SERVER_CERT) { |f|
- OpenSSL::X509::Certificate.new(f)
- }
- sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- sock.sync_close = true
- begin
- sock.accept
- 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)
- host, port = process_port_or_eprt(sock, line)
- 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|
- conn.print(s)
- end
- conn.close
- sock.print("226 Transfer complete.\r\n")
- rescue OpenSSL::SSL::SSLError
- end
- ensure
- sock.close
- server.close
- end
- end
- ftp = Net::FTP.new(SERVER_NAME,
- port: port,
- ssl: { ca_file: CA_FILE },
- private_data_connection: false,
- passive: false)
- begin
- assert_equal("AUTH TLS\r\n", commands.shift)
- ftp.login
- assert_match(/\AUSER /, commands.shift)
- assert_match(/\APASS /, commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- buf = ftp.getbinaryfile("foo", nil)
- assert_equal(binary_data, buf)
- assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("RETR foo\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close
- end
- end
-
- def test_passive_clear_data_connection
- server = TCPServer.new(SERVER_ADDR, 0)
- port = server.addr[1]
- commands = []
- binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
- @thread = Thread.start do
- sock = server.accept
- begin
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("234 AUTH success.\r\n")
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ca_file = CA_FILE
- ctx.key = File.open(SERVER_KEY) { |f|
- OpenSSL::PKey::RSA.new(f)
- }
- ctx.cert = File.open(SERVER_CERT) { |f|
- OpenSSL::X509::Certificate.new(f)
- }
- sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- sock.sync_close = true
- begin
- sock.accept
- 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)
- data_server = create_data_connection_server(sock)
- commands.push(sock.gets)
- sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
- conn = data_server.accept
- binary_data.scan(/.{1,1024}/nm) do |s|
- conn.print(s)
- end
- conn.close
- data_server.close
- sock.print("226 Transfer complete.\r\n")
- rescue OpenSSL::SSL::SSLError
- end
- ensure
- sock.close
- server.close
- end
- end
- ftp = Net::FTP.new(SERVER_NAME,
- port: port,
- ssl: { ca_file: CA_FILE },
- private_data_connection: false,
- passive: true)
- begin
- assert_equal("AUTH TLS\r\n", commands.shift)
- ftp.login
- assert_match(/\AUSER /, commands.shift)
- assert_match(/\APASS /, commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- buf = ftp.getbinaryfile("foo", nil)
- assert_equal(binary_data, buf)
- assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_match(/\A(PASV|EPSV)\r\n/, commands.shift)
- assert_equal("RETR foo\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close
- end
- end
-
- def test_tls_connect_timeout
- server = TCPServer.new(SERVER_ADDR, 0)
- port = server.addr[1]
- commands = []
- sock = nil
- @thread = Thread.start do
- sock = server.accept
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("234 AUTH success.\r\n")
- end
- begin
- assert_raise(Net::OpenTimeout) do
- Net::FTP.new(SERVER_NAME,
- port: port,
- ssl: { ca_file: CA_FILE },
- ssl_handshake_timeout: 0.1)
- end
- @thread.join
- ensure
- sock.close if sock
- server.close
- end
- end
- end
-
- def test_abort_tls
- return unless defined?(OpenSSL)
-
- commands = []
- server = create_ftp_server { |sock|
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("234 AUTH success.\r\n")
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ca_file = CA_FILE
- ctx.key = File.open(SERVER_KEY) { |f|
- OpenSSL::PKey::RSA.new(f)
- }
- ctx.cert = File.open(SERVER_CERT) { |f|
- OpenSSL::X509::Certificate.new(f)
- }
- sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- sock.sync_close = true
- sock.accept
- commands.push(sock.gets)
- sock.print("200 PSBZ success.\r\n")
- commands.push(sock.gets)
- sock.print("200 PROT success.\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("225 No transfer to ABOR.\r\n")
- }
- begin
- begin
- ftp = Net::FTP.new(SERVER_NAME,
- port: server.port,
- ssl: { ca_file: CA_FILE })
- assert_equal("AUTH TLS\r\n", commands.shift)
- assert_equal("PBSZ 0\r\n", commands.shift)
- assert_equal("PROT P\r\n", commands.shift)
- 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\n", commands.shift)
- assert_equal(nil, commands.shift)
- rescue RuntimeError, LoadError
- # skip (require openssl)
- ensure
- ftp.close if ftp
- end
- ensure
- server.close
- end
- end
-
- def test_getbinaryfile_command_injection
- skip "| is not allowed in filename on Windows" if windows?
- [false, true].each do |resume|
- 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)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("150 Opening BINARY mode data connection for |echo hello (#{binary_data.size} bytes)\r\n")
- conn = TCPSocket.new(host, port)
- binary_data.scan(/.{1,1024}/nm) do |s|
- conn.print(s)
- end
- conn.shutdown(Socket::SHUT_WR)
- conn.read
- conn.close
- sock.print("226 Transfer complete.\r\n")
- }
- begin
- chdir_to_tmpdir do
- begin
- ftp = Net::FTP.new
- ftp.resume = resume
- ftp.read_timeout = RubyVM::MJIT.enabled? ? 5 : 0.2 # use large timeout for --jit-wait
- 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.getbinaryfile("|echo hello")
- assert_equal(binary_data, File.binread("./|echo hello"))
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("RETR |echo hello\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- end
- ensure
- server.close
- end
- end
- end
-
- def test_gettextfile_command_injection
- skip "| is not allowed in filename on Windows" if windows?
- commands = []
- text_data = <<EOF.gsub(/\n/, "\r\n")
-foo
-bar
-baz
-EOF
- 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)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("150 Opening TEXT mode data connection for |echo hello (#{text_data.size} bytes)\r\n")
- conn = TCPSocket.new(host, port)
- text_data.each_line do |l|
- conn.print(l)
- end
- conn.shutdown(Socket::SHUT_WR)
- conn.read
- conn.close
- sock.print("226 Transfer complete.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to Binary mode.\r\n")
- }
- begin
- chdir_to_tmpdir do
- 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)
- ftp.gettextfile("|echo hello")
- assert_equal(text_data.gsub(/\r\n/, "\n"),
- File.binread("./|echo hello"))
- assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("RETR |echo hello\r\n", commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- end
- ensure
- server.close
- end
- end
-
- def test_putbinaryfile_command_injection
- skip "| is not allowed in filename on Windows" if windows?
- commands = []
- binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
- received_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)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("150 Opening BINARY mode data connection for |echo hello (#{binary_data.size} bytes)\r\n")
- conn = TCPSocket.new(host, port)
- received_data = conn.read
- conn.close
- sock.print("226 Transfer complete.\r\n")
- }
- begin
- chdir_to_tmpdir do
- File.binwrite("./|echo hello", binary_data)
- 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.putbinaryfile("|echo hello")
- assert_equal(binary_data, received_data)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("STOR |echo hello\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- end
- ensure
- server.close
- end
- end
-
- def test_puttextfile_command_injection
- skip "| is not allowed in filename on Windows" if windows?
- commands = []
- received_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")
- commands.push(sock.gets)
- sock.print("200 Switching to ASCII mode.\r\n")
- line = sock.gets
- commands.push(line)
- host, port = process_port_or_eprt(sock, line)
- commands.push(sock.gets)
- sock.print("150 Opening TEXT mode data connection for |echo hello\r\n")
- conn = TCPSocket.new(host, port)
- received_data = conn.read
- conn.close
- sock.print("226 Transfer complete.\r\n")
- commands.push(sock.gets)
- sock.print("200 Switching to Binary mode.\r\n")
- }
- begin
- chdir_to_tmpdir do
- File.open("|echo hello", "w") do |f|
- f.puts("foo")
- f.puts("bar")
- f.puts("baz")
- end
- 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)
- ftp.puttextfile("|echo hello")
- assert_equal(<<EOF.gsub(/\n/, "\r\n"), received_data)
-foo
-bar
-baz
-EOF
- assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\A(PORT|EPRT) /, commands.shift)
- assert_equal("STOR |echo hello\r\n", commands.shift)
- assert_equal("TYPE I\r\n", commands.shift)
- assert_equal(nil, commands.shift)
- ensure
- ftp.close if ftp
- end
- end
- ensure
- server.close
- end
- end
-
- private
-
- def create_ftp_server(sleep_time = nil)
- server = TCPServer.new(SERVER_ADDR, 0)
- @thread = Thread.start do
- if sleep_time
- sleep(sleep_time)
- end
- sock = server.accept
- begin
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1)
- yield(sock)
- sock.shutdown(Socket::SHUT_WR)
- sock.read unless sock.eof?
- ensure
- sock.close
- end
- end
- def server.port
- addr[1]
- end
- return server
- end
-
- def tls_test
- server = TCPServer.new(SERVER_ADDR, 0)
- port = server.addr[1]
- commands = []
- @thread = Thread.start do
- sock = server.accept
- begin
- sock.print("220 (test_ftp).\r\n")
- commands.push(sock.gets)
- sock.print("234 AUTH success.\r\n")
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ca_file = CA_FILE
- ctx.key = File.open(SERVER_KEY) { |f|
- OpenSSL::PKey::RSA.new(f)
- }
- ctx.cert = File.open(SERVER_CERT) { |f|
- OpenSSL::X509::Certificate.new(f)
- }
- sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- sock.sync_close = true
- begin
- sock.accept
- commands.push(sock.gets)
- sock.print("200 PSBZ success.\r\n")
- commands.push(sock.gets)
- sock.print("200 PROT success.\r\n")
- rescue OpenSSL::SSL::SSLError, SystemCallError
- end
- ensure
- sock.close
- server.close
- end
- end
- ftp = yield(port)
- ftp.close
-
- assert_equal("AUTH TLS\r\n", commands.shift)
- assert_equal("PBSZ 0\r\n", commands.shift)
- assert_equal("PROT P\r\n", commands.shift)
- end
-
- def process_port_or_eprt(sock, line)
- case line
- when /\APORT (.*)/
- port_args = $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")
- return host, port
- when /\AEPRT \|2\|(.*?)\|(.*?)\|/
- host = $1
- port = $2.to_i
- sock.print("200 EPRT command successful.\r\n")
- return host, port
- else
- flunk "PORT or EPRT expected"
- end
- end
-
- def create_data_connection_server(sock)
- data_server = TCPServer.new(SERVER_ADDR, 0)
- port = data_server.local_address.ip_port
- if data_server.local_address.ipv4?
- sock.printf("227 Entering Passive Mode (127,0,0,1,%s).\r\n",
- port.divmod(256).join(","))
- elsif data_server.local_address.ipv6?
- sock.printf("229 Entering Extended Passive Mode (|||%d|)\r\n", port)
- else
- flunk "Invalid local address"
- end
- return data_server
- end
-
- def chdir_to_tmpdir
- Dir.mktmpdir do |dir|
- pwd = Dir.pwd
- Dir.chdir(dir)
- begin
- yield
- ensure
- Dir.chdir(pwd)
- end
- end
- end
-end
diff --git a/test/net/ftp/test_mlsx_entry.rb b/test/net/ftp/test_mlsx_entry.rb
deleted file mode 100644
index 92fe411548..0000000000
--- a/test/net/ftp/test_mlsx_entry.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-# frozen_string_literal: true
-
-require "net/ftp"
-require "test/unit"
-require "ostruct"
-require "stringio"
-
-class MLSxEntryTest < Test::Unit::TestCase
- def test_file?
- assert_equal(true, Net::FTP::MLSxEntry.new({"type"=>"file"}, "foo").file?)
- assert_equal(false, Net::FTP::MLSxEntry.new({"type"=>"dir"}, "foo").file?)
- assert_equal(false, Net::FTP::MLSxEntry.new({"type"=>"cdir"}, "foo").file?)
- assert_equal(false, Net::FTP::MLSxEntry.new({"type"=>"pdir"}, "foo").file?)
- end
-
- def test_directory?
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"type"=>"file"}, "foo").directory?)
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"type"=>"dir"}, "foo").directory?)
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"type"=>"cdir"}, "foo").directory?)
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"type"=>"pdir"}, "foo").directory?)
- end
-
- def test_appendable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"a"}, "foo").appendable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").appendable?)
- end
-
- def test_creatable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"c"}, "foo").creatable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").creatable?)
- end
-
- def test_deletable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"d"}, "foo").deletable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").deletable?)
- end
-
- def test_enterable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"e"}, "foo").enterable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").enterable?)
- end
-
- def test_renamable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"f"}, "foo").renamable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").renamable?)
- end
-
- def test_listable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"l"}, "foo").listable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").listable?)
- end
-
- def test_directory_makable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"m"}, "foo").
- directory_makable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").
- directory_makable?)
- end
-
- def test_purgeable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"p"}, "foo").purgeable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").purgeable?)
- end
-
- def test_readable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"r"}, "foo").readable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").readable?)
- end
-
- def test_writable?
- assert_equal(true,
- Net::FTP::MLSxEntry.new({"perm"=>"w"}, "foo").writable?)
- assert_equal(false,
- Net::FTP::MLSxEntry.new({"perm"=>""}, "foo").writable?)
- end
-end
diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb
index 0344ad786b..4e7fa22756 100644
--- a/test/net/http/test_http.rb
+++ b/test/net/http/test_http.rb
@@ -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
@@ -115,7 +115,7 @@ class TestNetHTTP < Test::Unit::TestCase
end
def test_proxy_address
- clean_http_proxy_env do
+ TestNetHTTPUtils.clean_http_proxy_env do
http = Net::HTTP.new 'hostname.example', nil, 'proxy.example'
assert_equal 'proxy.example', http.proxy_address
@@ -125,17 +125,17 @@ class TestNetHTTP < Test::Unit::TestCase
end
def test_proxy_address_no_proxy
- clean_http_proxy_env do
- http = Net::HTTP.new 'hostname.example', nil, 'proxy.example', nil, nil, nil, 'example'
+ 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.example', nil, nil, nil, 'example,10.224.0.0/22'
+ 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
- clean_http_proxy_env do
+ TestNetHTTPUtils.clean_http_proxy_env do
ENV['http_proxy'] = 'http://proxy.example:8000'
assert_equal false, Net::HTTP.proxy_class?
@@ -146,7 +146,7 @@ class TestNetHTTP < Test::Unit::TestCase
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 'hostname.example'
@@ -156,13 +156,13 @@ class TestNetHTTP < Test::Unit::TestCase
end
def test_proxy_eh_no_proxy
- clean_http_proxy_env do
+ 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 'hostname.example'
@@ -172,30 +172,37 @@ class TestNetHTTP < Test::Unit::TestCase
end
def test_proxy_eh_ENV_with_user
- clean_http_proxy_env do
+ 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?
- if Net::HTTP::ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE
- assert_equal 'foo', http.proxy_user
- assert_equal 'bar', http.proxy_pass
- else
- assert_nil http.proxy_user
- assert_nil http.proxy_pass
- end
+ 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
+ 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'] = 'hostname.example'
@@ -204,7 +211,7 @@ class TestNetHTTP < Test::Unit::TestCase
end
def test_proxy_port
- clean_http_proxy_env do
+ 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
@@ -216,7 +223,7 @@ class TestNetHTTP < Test::Unit::TestCase
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 'hostname.example'
@@ -226,7 +233,7 @@ class TestNetHTTP < Test::Unit::TestCase
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 'hostname.example'
@@ -235,25 +242,6 @@ class TestNetHTTP < Test::Unit::TestCase
end
end
- def clean_http_proxy_env
- orig = {
- 'http_proxy' => ENV['http_proxy'],
- 'http_proxy_user' => ENV['http_proxy_user'],
- 'http_proxy_pass' => ENV['http_proxy_pass'],
- 'no_proxy' => ENV['no_proxy'],
- }
-
- orig.each_key do |key|
- ENV.delete key
- end
-
- yield
- ensure
- orig.each do |key, value|
- ENV[key] = value
- end
- end
-
def test_failure_message_includes_failed_domain_and_port
# hostname to be included in the error message
host = Struct.new(:to_s).new("<example>")
@@ -262,10 +250,22 @@ class TestNetHTTP < Test::Unit::TestCase
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
- clean_http_proxy_env{ Net::HTTP.get(uri) }
+ 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
+
+ http.open_timeout = 10
+ assert_equal 10, http.open_timeout
+ ensure
+ Net::HTTP.default_configuration = nil
+ end
+
end
module TestNetHTTP_version_1_1_methods
@@ -302,6 +302,27 @@ module TestNetHTTP_version_1_1_methods
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
@@ -433,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
@@ -470,9 +495,9 @@ module TestNetHTTP_version_1_1_methods
def test_s_post
url = "http://#{config('host')}:#{config('port')}/?q=a"
res = Net::HTTP.post(
- URI.parse(url),
- "a=x")
- assert_equal "application/x-www-form-urlencoded", res["Content-Type"]
+ 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"]
@@ -538,11 +563,13 @@ module TestNetHTTP_version_1_1_methods
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(0.1)
+ conn.open_timeout = EnvUtil.apply_timeout_scale(1)
th = Thread.new do
err = !windows? ? Net::WriteTimeout : Net::ReadTimeout
- assert_raise(err) { conn.post('/', "a"*50_000_000) }
+ assert_raise(err) do
+ conn.post('/', "a"*50_000_000)
+ end
end
assert th.join(EnvUtil.apply_timeout_scale(10))
}
@@ -551,6 +578,33 @@ module TestNetHTTP_version_1_1_methods
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]"
@@ -561,7 +615,7 @@ module TestNetHTTP_version_1_1_methods
conn = Net::HTTP.new('localhost', port)
conn.read_timeout = EnvUtil.apply_timeout_scale(0.01)
- conn.open_timeout = EnvUtil.apply_timeout_scale(0.1)
+ conn.open_timeout = EnvUtil.apply_timeout_scale(1)
th = Thread.new do
assert_raise(Net::ReadTimeout) {
@@ -587,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)
@@ -801,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)
}
}
@@ -845,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')
@@ -860,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
@@ -884,6 +948,17 @@ 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
@@ -931,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
@@ -986,10 +1061,10 @@ class TestNetHTTPContinue < Test::Unit::TestCase
def test_expect_continue_error_before_body
@log_tester = nil
mount_proc {|req, res|
- raise WEBrick::HTTPStatus::Forbidden
+ raise TestNetHTTPUtils::Forbidden
}
start {|http|
- uheader = {'content-length' => '5', 'expect' => '100-continue'}
+ 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')
@@ -1031,7 +1106,7 @@ class TestNetHTTPSwitchingProtocols < 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_info
@@ -1041,7 +1116,8 @@ class TestNetHTTPSwitchingProtocols < Test::Unit::TestCase
}
start {|http|
http.continue_timeout = 0.2
- http.request_post('/continue', 'body=BODY') {|res|
+ http.request_post('/continue', 'body=BODY',
+ 'content-type' => 'application/x-www-form-urlencoded') {|res|
assert_equal('BODY', res.read_body)
}
}
@@ -1104,6 +1180,30 @@ 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)
@@ -1156,6 +1256,16 @@ class TestNetHTTPKeepAlive < Test::Unit::TestCase
}
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
@@ -1206,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 b7515b7e98..9f5cf4f8f5 100644
--- a/test/net/http/test_http_request.rb
+++ b/test/net/http/test_http_request.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: false
require 'net/http'
require 'test/unit'
-require 'stringio'
class HTTPRequestTest < Test::Unit::TestCase
@@ -47,8 +46,9 @@ class HTTPRequestTest < Test::Unit::TestCase
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
@@ -74,7 +74,20 @@ class HTTPRequestTest < Test::Unit::TestCase
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
@@ -88,5 +101,25 @@ class HTTPRequestTest < Test::Unit::TestCase
'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 cb4678a805..69563168db 100644
--- a/test/net/http/test_httpheader.rb
+++ b/test/net/http/test_httpheader.rb
@@ -28,7 +28,11 @@ class HTTPHeaderTest < Test::Unit::TestCase
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") }
- assert_raise(ArgumentError){ @c.initialize_http_header("foo"=>"a\xff") }
+ 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
@@ -119,7 +123,19 @@ class HTTPHeaderTest < Test::Unit::TestCase
class D; include Net::HTTPHeader; end
def test_nil_variable_header
- assert_nothing_raised { D.new.initialize_http_header({Authorization: nil}) }
+ 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
@@ -296,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
@@ -305,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 a03bb2e152..01281063cd 100644
--- a/test/net/http/test_httpresponse.rb
+++ b/test/net/http/test_httpresponse.rb
@@ -54,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
@@ -77,6 +312,10 @@ EOS
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
@@ -123,9 +362,11 @@ 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
@@ -151,9 +392,11 @@ 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
@@ -184,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
@@ -211,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
@@ -234,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
@@ -284,9 +532,11 @@ EOS
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
@@ -310,9 +560,11 @@ EOS
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
@@ -337,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'
diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb
index c1d486470a..f5b21b901f 100644
--- a/test/net/http/test_https.rb
+++ b/test/net/http/test_https.rb
@@ -1,113 +1,156 @@
# frozen_string_literal: false
require "test/unit"
+require_relative "utils"
begin
require 'net/https'
- require 'stringio'
- require 'timeout'
- 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
- def self.fixture(key)
+ def self.read_fixture(key)
File.read(File.expand_path("../fixtures/#{key}", __dir__))
end
- CA_CERT = OpenSSL::X509::Certificate.new(fixture("cacert.pem"))
- SERVER_KEY = OpenSSL::PKey.read(fixture("server.key"))
- SERVER_CERT = OpenSSL::X509::Certificate.new(fixture("server.crt"))
- DHPARAMS = OpenSSL::PKey::DH.new(fixture("dhparams.pem"))
+ 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',
+ 'host' => HOST,
'proxy_host' => nil,
'proxy_port' => nil,
'ssl_enable' => true,
'ssl_certificate' => SERVER_CERT,
'ssl_private_key' => SERVER_KEY,
- 'ssl_tmp_dh_callback' => proc { DHPARAMS },
}
def test_get
- http = Net::HTTP.new("localhost", config("port"))
+ http = Net::HTTP.new(HOST, config("port"))
+ http.use_ssl = true
+ 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
+ http = Net::HTTP.new(HOST, config("port"))
+ http.ipaddr = config('host')
http.use_ssl = true
http.cert_store = TEST_STORE
- certs = []
- http.verify_callback = Proc.new do |preverify_ok, store_ctx|
- certs << store_ctx.current_cert
- preverify_ok
- end
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])
}
- assert_equal(CA_CERT.to_der, certs[0].to_der)
- assert_equal(SERVER_CERT.to_der, certs[1].to_der)
- rescue SystemCallError
- skip $!
+
+ 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("localhost", config("port"))
+ 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, {'content-type' => 'application/x-www-form-urlencoded'}) {|res|
assert_equal(data, res.body)
}
- rescue SystemCallError
- skip $!
end
def test_session_reuse
- # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
- # See https://github.com/openssl/openssl/pull/5967 for details.
- skip if OpenSSL::OPENSSL_LIBRARY_VERSION =~ /OpenSSL 1.1.0h/
-
- http = Net::HTTP.new("localhost", config("port"))
+ http = Net::HTTP.new(HOST, config("port"))
http.use_ssl = true
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("/")
-
- socket = http.instance_variable_get(:@socket).io
- assert_equal true, socket.session_reused?
-
+ 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
- rescue SystemCallError
- skip $!
end
def test_session_reuse_but_expire
- # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
- skip if OpenSSL::OPENSSL_LIBRARY_VERSION =~ /OpenSSL 1.1.0h/
-
- http = Net::HTTP.new("localhost", config("port"))
+ http = Net::HTTP.new(HOST, config("port"))
http.use_ssl = true
http.cert_store = TEST_STORE
- http.ssl_timeout = -1
+ 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?
+ assert_equal false, socket.session_reused?, "NOTE: OpenSSL library version is #{OpenSSL::OPENSSL_LIBRARY_VERSION}"
http.finish
- rescue SystemCallError
- skip $!
end
if ENV["RUBY_OPENSSL_TEST_ALL"]
@@ -122,58 +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)
- unless /mswin|mingw/ =~ RUBY_PLATFORM
- # on Windows, Errno::ECONNRESET will be raised, and it'll be eaten by
- # WEBrick
- @log_tester = lambda {|log|
- assert_equal(1, log.length)
- assert_match(/ERROR OpenSSL::SSL::SSLError:/, log[0])
- }
- end
end
- def test_identity_verify_failure
- # the certificate's subject has CN=localhost
- 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.cert_store = TEST_STORE
- @log_tester = lambda {|_| }
- ex = assert_raise(OpenSSL::SSL::SSLError){
- http.request_get("/") {|res| }
+ certs = []
+ http.verify_callback = Proc.new {|preverify_ok, store_ctx|
+ certs << store_ctx.current_cert
+ preverify_ok
}
- re_msg = /certificate verify failed|hostname \"127.0.0.1\" does not match/
- assert_match(re_msg, ex.message)
+ http.request_get("/") {|res|
+ assert_equal($test_net_http_data, res.body)
+ }
+ 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
@@ -188,7 +244,7 @@ class TestNetHTTPS < Test::Unit::TestCase
end
def test_min_version
- http = Net::HTTP.new("localhost", config("port"))
+ http = Net::HTTP.new(HOST, config("port"))
http.use_ssl = true
http.min_version = :TLS1
http.cert_store = TEST_STORE
@@ -198,18 +254,72 @@ class TestNetHTTPS < Test::Unit::TestCase
end
def test_max_version
- http = Net::HTTP.new("127.0.0.1", config("port"))
+ http = Net::HTTP.new(HOST, config("port"))
http.use_ssl = true
http.max_version = :SSL2
- http.verify_callback = Proc.new do |preverify_ok, store_ctx|
- true
- end
+ 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/
+ re_msg = /\ASSL_connect returned=1 errno=0 |SSL_CTX_set_max_proto_version|No appropriate protocol/
assert_match(re_msg, ex.message)
end
-end if defined?(OpenSSL::SSL)
+ 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 f833f1a1e3..237c16e64d 100644
--- a/test/net/http/test_https_proxy.rb
+++ b/test/net/http/test_https_proxy.rb
@@ -5,14 +5,10 @@ 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
-
TCPServer.open("127.0.0.1", 0) {|serv|
_, port, _, _ = serv.addr
client_thread = Thread.new {
@@ -43,5 +39,46 @@ class HTTPSProxyTest < Test::Unit::TestCase
assert_join_threads([client_thread, server_thread])
}
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 dbfd112f31..0b9e440e7c 100644
--- a/test/net/http/utils.rb
+++ b/test/net/http/utils.rb
@@ -1,13 +1,234 @@
# frozen_string_literal: false
-require 'webrick'
-begin
- require "webrick/https"
-rescue LoadError
- # SSL features cannot be tested
-end
-require 'webrick/httpservlet/abstract'
+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
@@ -15,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
@@ -25,7 +246,7 @@ module TestNetHTTPUtils
end
def logfile
- $DEBUG ? $stderr : NullWriter.new
+ $stderr if $DEBUG
end
def setup
@@ -33,78 +254,106 @@ module TestNetHTTPUtils
end
def teardown
+ sleep 0.5 if @config['ssl_enable']
if @server
@server.shutdown
- @server_thread.join
- WEBrick::Utils::TimeoutHandler.terminate
end
@log_tester.call(@log) if @log_tester
- # resume global state
Net::HTTP.version_1_2
end
def spawn_server
@log = []
- @log_tester = lambda {|log| assert_equal([], log ) }
+ @log_tester = lambda {|log| assert_equal([], log) }
@config = self.class::CONFIG
- server_config = {
- :BindAddress => config('host'),
- :Port => 0,
- :Logger => WEBrick::Log.new(@log, WEBrick::BasicLog::WARN),
- :AccessLog => [],
- :ServerType => Thread,
- }
- server_config[:OutputBufferSize] = 4 if config('chunked')
- server_config[:RequestTimeout] = config('RequestTimeout') if config('RequestTimeout')
- if defined?(OpenSSL) and config('ssl_enable')
- server_config.update({
- :SSLEnable => true,
- :SSLCertificate => config('ssl_certificate'),
- :SSLPrivateKey => config('ssl_private_key'),
- :SSLTmpDhCallback => config('ssl_tmp_dh_callback'),
- })
- end
- @server = WEBrick::HTTPServer.new(server_config)
- @server.mount('/', Servlet, config('chunked'))
- @server_thread = @server.start
- @config['port'] = @server[:Port]
+ @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
+ 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/test_imap.rb b/test/net/imap/test_imap.rb
deleted file mode 100644
index b97be4ffb3..0000000000
--- a/test/net/imap/test_imap.rb
+++ /dev/null
@@ -1,785 +0,0 @@
-# frozen_string_literal: true
-
-require "net/imap"
-require "test/unit"
-
-class IMAPTest < Test::Unit::TestCase
- CA_FILE = File.expand_path("../fixtures/cacert.pem", __dir__)
- SERVER_KEY = File.expand_path("../fixtures/server.key", __dir__)
- SERVER_CERT = File.expand_path("../fixtures/server.crt", __dir__)
-
- def setup
- @do_not_reverse_lookup = Socket.do_not_reverse_lookup
- Socket.do_not_reverse_lookup = true
- @threads = []
- end
-
- def teardown
- if !@threads.empty?
- assert_join_threads(@threads)
- end
- ensure
- Socket.do_not_reverse_lookup = @do_not_reverse_lookup
- end
-
- def test_encode_utf7
- assert_equal("foo", Net::IMAP.encode_utf7("foo"))
- assert_equal("&-", Net::IMAP.encode_utf7("&"))
-
- utf8 = "\357\274\241\357\274\242\357\274\243".dup.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&".dup.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".dup.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 start_server
- th = Thread.new do
- yield
- end
- @threads << th
- sleep 0.1 until th.stop?
- end
-
- def test_unexpected_eof
- server = create_tcp_server
- port = server.addr[1]
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- sock.gets
-# sock.print("* BYE terminating connection\r\n")
-# sock.print("RUBY0001 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- assert_raise(EOFError) do
- imap.logout
- end
- ensure
- imap.disconnect if imap
- end
- end
-
- def test_idle
- server = create_tcp_server
- port = server.addr[1]
- requests = []
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- requests.push(sock.gets)
- sock.print("+ idling\r\n")
- sock.print("* 3 EXISTS\r\n")
- sock.print("* 2 EXPUNGE\r\n")
- requests.push(sock.gets)
- sock.print("RUBY0001 OK IDLE terminated\r\n")
- sock.gets
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0002 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
-
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- responses = []
- imap.idle do |res|
- responses.push(res)
- if res.name == "EXPUNGE"
- imap.idle_done
- end
- end
- assert_equal(3, responses.length)
- assert_instance_of(Net::IMAP::ContinuationRequest, responses[0])
- assert_equal("EXISTS", responses[1].name)
- assert_equal(3, responses[1].data)
- assert_equal("EXPUNGE", responses[2].name)
- assert_equal(2, responses[2].data)
- assert_equal(2, requests.length)
- assert_equal("RUBY0001 IDLE\r\n", requests[0])
- assert_equal("DONE\r\n", requests[1])
- imap.logout
- ensure
- imap.disconnect if imap
- end
- end
-
- def test_exception_during_idle
- server = create_tcp_server
- port = server.addr[1]
- requests = []
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- requests.push(sock.gets)
- sock.print("+ idling\r\n")
- sock.print("* 3 EXISTS\r\n")
- sock.print("* 2 EXPUNGE\r\n")
- requests.push(sock.gets)
- sock.print("RUBY0001 OK IDLE terminated\r\n")
- sock.gets
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0002 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- begin
- th = Thread.current
- m = Monitor.new
- in_idle = false
- exception_raised = false
- c = m.new_cond
- raiser = Thread.start do
- m.synchronize do
- until in_idle
- c.wait(0.1)
- end
- end
- th.raise(Interrupt)
- m.synchronize do
- exception_raised = true
- c.signal
- end
- end
- @threads << raiser
- 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
- raiser.kill unless in_idle
- end
- end
-
- def test_idle_done_not_during_idle
- server = create_tcp_server
- port = server.addr[1]
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- ensure
- sock.close
- server.close
- end
- end
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- assert_raise(Net::IMAP::Error) do
- imap.idle_done
- end
- ensure
- imap.disconnect if imap
- end
- end
-
- def test_idle_timeout
- server = create_tcp_server
- port = server.addr[1]
- requests = []
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- requests.push(sock.gets)
- sock.print("+ idling\r\n")
- sock.print("* 3 EXISTS\r\n")
- sock.print("* 2 EXPUNGE\r\n")
- requests.push(sock.gets)
- sock.print("RUBY0001 OK IDLE terminated\r\n")
- sock.gets
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0002 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
-
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- responses = []
- Thread.pass
- imap.idle(0.2) do |res|
- responses.push(res)
- end
- # There is no guarantee that this thread has received all the responses,
- # so check the response length.
- if responses.length > 0
- assert_instance_of(Net::IMAP::ContinuationRequest, responses[0])
- if responses.length > 1
- assert_equal("EXISTS", responses[1].name)
- assert_equal(3, responses[1].data)
- if responses.length > 2
- assert_equal("EXPUNGE", responses[2].name)
- assert_equal(2, responses[2].data)
- end
- end
- end
- # Also, there is no guarantee that the server thread has stored
- # all the requests into the array, so check the length.
- if requests.length > 0
- assert_equal("RUBY0001 IDLE\r\n", requests[0])
- if requests.length > 1
- assert_equal("DONE\r\n", requests[1])
- end
- end
- imap.logout
- ensure
- imap.disconnect if imap
- end
- end
-
- def test_unexpected_bye
- server = create_tcp_server
- port = server.addr[1]
- start_server do
- sock = server.accept
- begin
- sock.print("* OK Gimap ready for requests from 75.101.246.151 33if2752585qyk.26\r\n")
- sock.gets
- sock.print("* BYE System Error 33if2752585qyk.26\r\n")
- ensure
- sock.close
- server.close
- end
- end
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- assert_raise(Net::IMAP::ByeResponseError) do
- imap.login("user", "password")
- end
- end
- end
-
- def test_exception_during_shutdown
- server = create_tcp_server
- port = server.addr[1]
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- sock.gets
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0001 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- imap.instance_eval do
- def @sock.shutdown(*args)
- super
- ensure
- raise "error"
- end
- end
- imap.logout
- ensure
- assert_raise(RuntimeError) do
- imap.disconnect
- end
- end
- end
-
- def test_connection_closed_during_idle
- server = create_tcp_server
- port = server.addr[1]
- requests = []
- sock = nil
- threads = []
- started = false
- threads << Thread.start do
- started = true
- begin
- sock = server.accept
- sock.print("* OK test server\r\n")
- requests.push(sock.gets)
- sock.print("+ idling\r\n")
- rescue IOError # sock is closed by another thread
- ensure
- server.close
- end
- end
- sleep 0.1 until started
- threads << Thread.start do
- imap = Net::IMAP.new(server_addr, :port => port)
- begin
- m = Monitor.new
- in_idle = false
- closed = false
- c = m.new_cond
- threads << Thread.start do
- m.synchronize do
- until in_idle
- c.wait(0.1)
- end
- end
- sock.close
- m.synchronize do
- closed = true
- c.signal
- end
- end
- assert_raise(EOFError) do
- imap.idle do |res|
- m.synchronize do
- in_idle = true
- c.signal
- until closed
- c.wait(0.1)
- end
- end
- end
- end
- assert_equal(1, requests.length)
- assert_equal("RUBY0001 IDLE\r\n", requests[0])
- ensure
- imap.disconnect if imap
- end
- end
- assert_join_threads(threads)
- ensure
- if sock && !sock.closed?
- sock.close
- end
- end
-
- def test_connection_closed_without_greeting
- server = create_tcp_server
- port = server.addr[1]
- start_server do
- begin
- sock = server.accept
- sock.close
- ensure
- server.close
- end
- end
- assert_raise(Net::IMAP::Error) do
- Net::IMAP.new(server_addr, :port => port)
- end
- end
-
- def test_default_port
- assert_equal(143, Net::IMAP.default_port)
- assert_equal(143, Net::IMAP.default_imap_port)
- assert_equal(993, Net::IMAP.default_tls_port)
- assert_equal(993, Net::IMAP.default_ssl_port)
- assert_equal(993, Net::IMAP.default_imaps_port)
- end
-
- def test_send_invalid_number
- server = create_tcp_server
- port = server.addr[1]
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- sock.gets
- sock.print("RUBY0001 OK TEST completed\r\n")
- sock.gets
- sock.print("RUBY0002 OK TEST completed\r\n")
- sock.gets
- sock.print("RUBY0003 OK TEST completed\r\n")
- sock.gets
- sock.print("RUBY0004 OK TEST completed\r\n")
- sock.gets
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0005 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- assert_raise(Net::IMAP::DataFormatError) do
- imap.send(:send_command, "TEST", -1)
- end
- imap.send(:send_command, "TEST", 0)
- imap.send(:send_command, "TEST", 4294967295)
- assert_raise(Net::IMAP::DataFormatError) do
- imap.send(:send_command, "TEST", 4294967296)
- end
- assert_raise(Net::IMAP::DataFormatError) do
- imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(-1))
- end
- assert_raise(Net::IMAP::DataFormatError) do
- imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(0))
- end
- imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(1))
- imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(4294967295))
- assert_raise(Net::IMAP::DataFormatError) do
- imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(4294967296))
- end
- imap.logout
- ensure
- imap.disconnect
- end
- end
-
- def test_send_literal
- server = create_tcp_server
- port = server.addr[1]
- requests = []
- literal = nil
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- line = sock.gets
- requests.push(line)
- size = line.slice(/{(\d+)}\r\n/, 1).to_i
- sock.print("+ Ready for literal data\r\n")
- literal = sock.read(size)
- requests.push(sock.gets)
- sock.print("RUBY0001 OK TEST completed\r\n")
- sock.gets
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0002 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- imap.send(:send_command, "TEST", ["\xDE\xAD\xBE\xEF".b])
- assert_equal(2, requests.length)
- assert_equal("RUBY0001 TEST ({4}\r\n", requests[0])
- assert_equal("\xDE\xAD\xBE\xEF".b, literal)
- assert_equal(")\r\n", requests[1])
- imap.logout
- ensure
- imap.disconnect
- end
- end
-
- def test_disconnect
- server = create_tcp_server
- port = server.addr[1]
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- sock.gets
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0001 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- imap.logout
- imap.disconnect
- assert_equal(true, imap.disconnected?)
- imap.disconnect
- assert_equal(true, imap.disconnected?)
- ensure
- imap.disconnect if imap && !imap.disconnected?
- end
- end
-
- def test_append
- server = create_tcp_server
- port = server.addr[1]
- mail = <<EOF.gsub(/\n/, "\r\n")
-From: shugo@example.com
-To: matz@example.com
-Subject: hello
-
-hello world
-EOF
- requests = []
- received_mail = nil
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- line = sock.gets
- requests.push(line)
- size = line.slice(/{(\d+)}\r\n/, 1).to_i
- sock.print("+ Ready for literal data\r\n")
- received_mail = sock.read(size)
- sock.gets
- sock.print("RUBY0001 OK APPEND completed\r\n")
- requests.push(sock.gets)
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0002 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
-
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- imap.append("INBOX", mail)
- assert_equal(1, requests.length)
- assert_equal("RUBY0001 APPEND INBOX {#{mail.size}}\r\n", requests[0])
- assert_equal(mail, received_mail)
- imap.logout
- assert_equal(2, requests.length)
- assert_equal("RUBY0002 LOGOUT\r\n", requests[1])
- ensure
- imap.disconnect if imap
- end
- end
-
- def test_append_fail
- server = create_tcp_server
- port = server.addr[1]
- mail = <<EOF.gsub(/\n/, "\r\n")
-From: shugo@example.com
-To: matz@example.com
-Subject: hello
-
-hello world
-EOF
- requests = []
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- requests.push(sock.gets)
- sock.print("RUBY0001 NO Mailbox doesn't exist\r\n")
- requests.push(sock.gets)
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0002 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
-
- begin
- imap = Net::IMAP.new(server_addr, :port => port)
- assert_raise(Net::IMAP::NoResponseError) do
- imap.append("INBOX", mail)
- end
- assert_equal(1, requests.length)
- assert_equal("RUBY0001 APPEND INBOX {#{mail.size}}\r\n", requests[0])
- imap.logout
- assert_equal(2, requests.length)
- assert_equal("RUBY0002 LOGOUT\r\n", requests[1])
- ensure
- imap.disconnect if imap
- end
- end
-
- private
-
- def imaps_test
- server = create_tcp_server
- port = server.addr[1]
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ca_file = CA_FILE
- ctx.key = File.open(SERVER_KEY) { |f|
- OpenSSL::PKey::RSA.new(f)
- }
- ctx.cert = File.open(SERVER_CERT) { |f|
- OpenSSL::X509::Certificate.new(f)
- }
- ssl_server = OpenSSL::SSL::SSLServer.new(server, ctx)
- started = false
- ths = Thread.start do
- Thread.current.report_on_exception = false # always join-ed
- begin
- started = true
- sock = ssl_server.accept
- begin
- sock.print("* OK test server\r\n")
- sock.gets
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0001 OK LOGOUT completed\r\n")
- ensure
- sock.close
- end
- rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
- end
- end
- sleep 0.1 until started
- begin
- begin
- imap = yield(port)
- imap.logout
- ensure
- imap.disconnect if imap
- end
- ensure
- ssl_server.close
- ths.join
- end
- end
-
- def starttls_test
- server = create_tcp_server
- port = server.addr[1]
- start_server do
- sock = server.accept
- begin
- sock.print("* OK test server\r\n")
- sock.gets
- sock.print("RUBY0001 OK completed\r\n")
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ca_file = CA_FILE
- ctx.key = File.open(SERVER_KEY) { |f|
- OpenSSL::PKey::RSA.new(f)
- }
- ctx.cert = File.open(SERVER_CERT) { |f|
- OpenSSL::X509::Certificate.new(f)
- }
- sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- sock.sync_close = true
- sock.accept
- sock.gets
- sock.print("* BYE terminating connection\r\n")
- sock.print("RUBY0002 OK LOGOUT completed\r\n")
- ensure
- sock.close
- server.close
- end
- end
- begin
- imap = yield(port)
- imap.logout if !imap.disconnected?
- ensure
- imap.disconnect if imap && !imap.disconnected?
- end
- end
-
- def create_tcp_server
- return TCPServer.new(server_addr, 0)
- end
-
- def server_addr
- Addrinfo.tcp("localhost", 0).ip_address
- 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 ed31a03f5a..0000000000
--- a/test/net/imap/test_imap_response_parser.rb
+++ /dev/null
@@ -1,324 +0,0 @@
-# frozen_string_literal: true
-
-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
- ensure
- $SAFE = 0
- end
-
- def test_flag_list_too_many_flags
- parser = Net::IMAP::ResponseParser.new
- assert_nothing_raised do
- 3.times do |i|
- parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* LIST (\\Foo#{i}) "." "INBOX"
-EOF
- end
- end
- assert_raise(Net::IMAP::FlagCountError) do
- parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* LIST (\\Foo3) "." "INBOX"
-EOF
- end
- end
-
- def test_flag_list_many_same_flags
- parser = Net::IMAP::ResponseParser.new
- assert_nothing_raised do
- 100.times do
- parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* LIST (\\Foo) "." "INBOX"
-EOF
- end
- end
- end
-
- def test_flag_xlist_inbox
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* XLIST (\\Inbox) "." "INBOX"
-EOF
- assert_equal [:Inbox], response.data.attr
- end
-
- def test_resp_text_code
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* OK [CLOSED] Previous mailbox closed.
-EOF
- assert_equal "CLOSED", response.data.code.name
- end
-
- def test_search_response
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* SEARCH
-EOF
- assert_equal [], response.data
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* SEARCH 1
-EOF
- assert_equal [1], response.data
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* SEARCH 1 2 3
-EOF
- assert_equal [1, 2, 3], response.data
- end
-
- def test_search_response_of_yahoo
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* SEARCH 1\s
-EOF
- assert_equal [1], response.data
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* SEARCH 1 2 3\s
-EOF
- assert_equal [1, 2, 3], response.data
- end
-
- def test_msg_att_extra_space
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* 1 FETCH (UID 92285)
-EOF
- assert_equal 92285, response.data.attr["UID"]
-
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* 1 FETCH (UID 92285 )
-EOF
- assert_equal 92285, response.data.attr["UID"]
- end
-
- def test_msg_att_parse_error
- parser = Net::IMAP::ResponseParser.new
- e = assert_raise(Net::IMAP::ResponseParseError) {
- parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* 123 FETCH (UNKNOWN 92285)
-EOF
- }
- assert_match(/ for \{123\}/, e.message)
- end
-
- def test_msg_att_rfc822_text
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* 123 FETCH (RFC822 {5}
-foo
-)
-EOF
- assert_equal("foo\r\n", response.data.attr["RFC822"])
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* 123 FETCH (RFC822[] {5}
-foo
-)
-EOF
- assert_equal("foo\r\n", response.data.attr["RFC822"])
- end
-
- # [Bug #6397] [ruby-core:44849]
- def test_body_type_attachment
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* 980 FETCH (UID 2862 BODYSTRUCTURE ((("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 416 21 NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 1493 32 NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "Boundary_(ID_IaecgfnXwG5bn3x8lIeGIQ)") NIL NIL)("MESSAGE" "RFC822" ("NAME" "Fw_ ____ _____ ____.eml") NIL NIL "7BIT" 1980088 NIL ("ATTACHMENT" ("FILENAME" "Fw_ ____ _____ ____.eml")) NIL) "MIXED" ("BOUNDARY" "Boundary_(ID_eDdLc/j0mBIzIlR191pHjA)") NIL NIL))
-EOF
- assert_equal("Fw_ ____ _____ ____.eml",
- response.data.attr["BODYSTRUCTURE"].parts[1].body.param["FILENAME"])
- end
-
- def assert_parseable(s)
- parser = Net::IMAP::ResponseParser.new
- parser.parse(s.gsub(/\n/, "\r\n").taint)
- end
-
- # [Bug #7146]
- def test_msg_delivery_status
- # This was part of a larger response that caused crashes, but this was the
- # minimal test case to demonstrate it
- assert_parseable <<EOF
-* 4902 FETCH (BODY (("MESSAGE" "DELIVERY-STATUS" NIL NIL NIL "7BIT" 324) "REPORT"))
-EOF
- end
-
- # [Bug #7147]
- def test_msg_with_message_rfc822_attachment
- assert_parseable <<EOF
-* 5441 FETCH (BODY ((("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 69 1)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 455 12) "ALTERNATIVE")("MESSAGE" "RFC822" ("NAME" "ATT00026.eml") NIL NIL "7BIT" 4079755) "MIXED"))
-EOF
- end
-
- # [Bug #7153]
- def test_msg_body_mixed
- assert_parseable <<EOF
-* 1038 FETCH (BODY ("MIXED"))
-EOF
- end
-
- # [Bug #8167]
- def test_msg_delivery_status_with_extra_data
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* 29021 FETCH (RFC822.SIZE 3162 UID 113622 RFC822.HEADER {1155}
-Return-path: <>
-Envelope-to: info@xxxxxxxx.si
-Delivery-date: Tue, 26 Mar 2013 12:42:58 +0100
-Received: from mail by xxxx.xxxxxxxxxxx.net with spam-scanned (Exim 4.76)
- id 1UKSHI-000Cwl-AR
- for info@xxxxxxxx.si; Tue, 26 Mar 2013 12:42:58 +0100
-X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on xxxx.xxxxxxxxxxx.net
-X-Spam-Level: **
-X-Spam-Status: No, score=2.1 required=7.0 tests=DKIM_ADSP_NXDOMAIN,RDNS_NONE
- autolearn=no version=3.3.1
-Received: from [xx.xxx.xxx.xx] (port=56890 helo=xxxxxx.localdomain)
- by xxxx.xxxxxxxxxxx.net with esmtp (Exim 4.76)
- id 1UKSHI-000Cwi-9j
- for info@xxxxxxxx.si; Tue, 26 Mar 2013 12:42:56 +0100
-Received: by xxxxxx.localdomain (Postfix)
- id 72725BEA64A; Tue, 26 Mar 2013 12:42:55 +0100 (CET)
-Date: Tue, 26 Mar 2013 12:42:55 +0100 (CET)
-From: MAILER-DAEMON@xxxxxx.localdomain (Mail Delivery System)
-Subject: Undelivered Mail Returned to Sender
-To: info@xxxxxxxx.si
-Auto-Submitted: auto-replied
-MIME-Version: 1.0
-Content-Type: multipart/report; report-type=delivery-status;
- boundary="27797BEA649.1364298175/xxxxxx.localdomain"
-Message-Id: <20130326114255.72725BEA64A@xxxxxx.localdomain>
-
- BODYSTRUCTURE (("text" "plain" ("charset" "us-ascii") NIL "Notification" "7bit" 510 14 NIL NIL NIL NIL)("message" "delivery-status" NIL NIL "Delivery report" "7bit" 410 NIL NIL NIL NIL)("text" "rfc822-headers" ("charset" "us-ascii") NIL "Undelivered Message Headers" "7bit" 612 15 NIL NIL NIL NIL) "report" ("report-type" "delivery-status" "boundary" "27797BEA649.1364298175/xxxxxx.localdomain") NIL NIL NIL))
-EOF
- delivery_status = response.data.attr["BODYSTRUCTURE"].parts[1]
- assert_equal("MESSAGE", delivery_status.media_type)
- assert_equal("DELIVERY-STATUS", delivery_status.subtype)
- assert_equal(nil, delivery_status.param)
- assert_equal(nil, delivery_status.content_id)
- assert_equal("Delivery report", delivery_status.description)
- assert_equal("7BIT", delivery_status.encoding)
- assert_equal(410, delivery_status.size)
- end
-
- # [Bug #8281]
- def test_acl
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
-* ACL "INBOX/share" "imshare2copy1366146467@xxxxxxxxxxxxxxxxxx.com" lrswickxteda
-EOF
- assert_equal("ACL", response.name)
- assert_equal(1, response.data.length)
- assert_equal("INBOX/share", response.data[0].mailbox)
- assert_equal("imshare2copy1366146467@xxxxxxxxxxxxxxxxxx.com",
- response.data[0].user)
- assert_equal("lrswickxteda", response.data[0].rights)
- end
-
- # [Bug #8415]
- def test_capability
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse("* CAPABILITY st11p00mm-iscream009 1Q49 XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN\r\n")
- assert_equal("CAPABILITY", response.name)
- assert_equal("AUTH=PLAIN", response.data.last)
- response = parser.parse("* CAPABILITY st11p00mm-iscream009 1Q49 XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN \r\n")
- assert_equal("CAPABILITY", response.name)
- assert_equal("AUTH=PLAIN", response.data.last)
- end
-
- def test_mixed_boundary
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse("* 2688 FETCH (UID 179161 BODYSTRUCTURE (" \
- "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"iso-8859-1\") NIL NIL \"QUOTED-PRINTABLE\" 200 4 NIL NIL NIL)" \
- "(\"MESSAGE\" \"DELIVERY-STATUS\" NIL NIL NIL \"7BIT\" 318 NIL NIL NIL)" \
- "(\"MESSAGE\" \"RFC822\" NIL NIL NIL \"7BIT\" 2177" \
- " (\"Tue, 11 May 2010 18:28:16 -0400\" \"Re: Welcome letter\" (" \
- "(\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
- "((\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
- "((\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
- "((\"Doretha\" NIL \"doretha.info\" \"xxxxxxxx.si\")) " \
- "NIL NIL " \
- "\"<AC1D15E06EA82F47BDE18E851CC32F330717704E@localdomain>\" " \
- "\"<AANLkTikKMev1I73L2E7XLjRs67IHrEkb23f7ZPmD4S_9@localdomain>\")" \
- " (\"MIXED\" (\"BOUNDARY\" \"000e0cd29212e3e06a0486590ae2\") NIL NIL)" \
- " 37 NIL NIL NIL)" \
- " \"REPORT\" (\"BOUNDARY\" \"16DuG.4XbaNOvCi.9ggvq.8Ipnyp3\" \"REPORT-TYPE\" \"delivery-status\") NIL NIL))\r\n")
- empty_part = response.data.attr['BODYSTRUCTURE'].parts[2]
- assert_equal(empty_part.lines, 37)
- assert_equal(empty_part.body.media_type, 'MULTIPART')
- assert_equal(empty_part.body.subtype, 'MIXED')
- assert_equal(empty_part.body.param['BOUNDARY'], '000e0cd29212e3e06a0486590ae2')
- end
-
- # [Bug #10112]
- def test_search_modseq
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse("* SEARCH 87216 87221 (MODSEQ 7667567)\r\n")
- assert_equal("SEARCH", response.name)
- assert_equal([87216, 87221], response.data)
- end
-
- # [Bug #11128]
- def test_body_ext_mpart_without_lang
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse("* 4 FETCH (BODY (((\"text\" \"plain\" (\"charset\" \"utf-8\") NIL NIL \"7bit\" 257 9 NIL NIL NIL NIL)(\"text\" \"html\" (\"charset\" \"utf-8\") NIL NIL \"quoted-printable\" 655 9 NIL NIL NIL NIL) \"alternative\" (\"boundary\" \"001a1137a5047848dd05157ddaa1\") NIL)(\"application\" \"pdf\" (\"name\" \"test.xml\" \"x-apple-part-url\" \"9D00D9A2-98AB-4EFB-85BA-FB255F8BF3D7\") NIL NIL \"base64\" 4383638 NIL (\"attachment\" (\"filename\" \"test.xml\")) NIL NIL) \"mixed\" (\"boundary\" \"001a1137a5047848e405157ddaa3\") NIL))\r\n")
- assert_equal("FETCH", response.name)
- body = response.data.attr["BODY"]
- assert_equal(nil, body.parts[0].disposition)
- assert_equal(nil, body.parts[0].language)
- assert_equal("ATTACHMENT", body.parts[1].disposition.dsp_type)
- assert_equal("test.xml", body.parts[1].disposition.param["FILENAME"])
- assert_equal(nil, body.parts[1].language)
- end
-
- # [Bug #13649]
- def test_status
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse("* STATUS INBOX (UIDNEXT 1 UIDVALIDITY 1234)\r\n")
- assert_equal("STATUS", response.name)
- assert_equal("INBOX", response.data.mailbox)
- assert_equal(1234, response.data.attr["UIDVALIDITY"])
- response = parser.parse("* STATUS INBOX (UIDNEXT 1 UIDVALIDITY 1234) \r\n")
- assert_equal("STATUS", response.name)
- assert_equal("INBOX", response.data.mailbox)
- assert_equal(1234, response.data.attr["UIDVALIDITY"])
- end
-
- # [Bug #10119]
- def test_msg_att_modseq_data
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse("* 1 FETCH (FLAGS (\Seen) MODSEQ (12345) UID 5)\r\n")
- assert_equal(12345, response.data.attr["MODSEQ"])
- end
-
- def test_continuation_request_without_response_text
- parser = Net::IMAP::ResponseParser.new
- response = parser.parse("+\r\n")
- assert_instance_of(Net::IMAP::ContinuationRequest, response)
- assert_equal(nil, response.data.code)
- assert_equal("", response.data.text)
- end
-end
diff --git a/test/net/pop/test_pop.rb b/test/net/pop/test_pop.rb
deleted file mode 100644
index f4c807a7a8..0000000000
--- a/test/net/pop/test_pop.rb
+++ /dev/null
@@ -1,166 +0,0 @@
-# frozen_string_literal: true
-require 'net/pop'
-require 'test/unit'
-require 'digest/md5'
-
-class TestPOP < Test::Unit::TestCase
- def setup
- @users = {'user' => 'pass' }
- @ok_user = 'user'
- @stamp_base = "#{$$}.#{Time.now.to_i}@localhost"
- end
-
- def test_pop_auth_ok
- pop_test(false) do |pop|
- assert_instance_of Net::POP3, pop
- assert_nothing_raised do
- pop.start(@ok_user, @users[@ok_user])
- end
- end
- end
-
- def test_pop_auth_ng
- pop_test(false) do |pop|
- assert_instance_of Net::POP3, pop
- assert_raise Net::POPAuthenticationError do
- pop.start(@ok_user, 'bad password')
- end
- end
- end
-
- def test_apop_ok
- pop_test(@stamp_base) do |pop|
- assert_instance_of Net::APOP, pop
- assert_nothing_raised do
- pop.start(@ok_user, @users[@ok_user])
- end
- end
- end
-
- def test_apop_ng
- pop_test(@stamp_base) do |pop|
- assert_instance_of Net::APOP, pop
- assert_raise Net::POPAuthenticationError do
- pop.start(@ok_user, 'bad password')
- end
- end
- end
-
- def test_apop_invalid
- pop_test("\x80"+@stamp_base) do |pop|
- assert_instance_of Net::APOP, pop
- assert_raise Net::POPAuthenticationError do
- pop.start(@ok_user, @users[@ok_user])
- end
- end
- end
-
- def test_apop_invalid_at
- pop_test(@stamp_base.sub('@', '.')) do |pop|
- assert_instance_of Net::APOP, pop
- assert_raise Net::POPAuthenticationError do
- pop.start(@ok_user, @users[@ok_user])
- end
- end
- end
-
- def test_popmail
- # totally not representative of real messages, but
- # enough to test frozen bugs
- lines = [ "[ruby-core:85210]" , "[Bug #14416]" ].freeze
- command = Object.new
- command.instance_variable_set(:@lines, lines)
-
- def command.retr(n)
- @lines.each { |l| yield "#{l}\r\n" }
- end
-
- def command.top(number, nl)
- @lines.each do |l|
- yield "#{l}\r\n"
- break if (nl -= 1) <= 0
- end
- end
-
- net_pop = :unused
- popmail = Net::POPMail.new(1, 123, net_pop, command)
- res = popmail.pop
- assert_equal "[ruby-core:85210]\r\n[Bug #14416]\r\n", res
- assert_not_predicate res, :frozen?
-
- res = popmail.top(1)
- assert_equal "[ruby-core:85210]\r\n", res
- assert_not_predicate res, :frozen?
- end
-
- def pop_test(apop=false)
- host = 'localhost'
- server = TCPServer.new(host, 0)
- port = server.addr[1]
- server_thread = Thread.start do
- sock = server.accept
- begin
- pop_server_loop(sock, apop)
- ensure
- sock.close
- end
- end
- client_thread = Thread.start do
- begin
- begin
- pop = Net::POP3::APOP(apop).new(host, port)
- #pop.set_debug_output $stderr
- yield pop
- ensure
- begin
- pop.finish
- rescue IOError
- raise unless $!.message == "POP session not yet started"
- end
- end
- ensure
- server.close
- end
- end
- assert_join_threads([client_thread, server_thread])
- end
-
- def pop_server_loop(sock, apop)
- if apop
- sock.print "+OK ready <#{apop}>\r\n"
- else
- sock.print "+OK ready\r\n"
- end
- user = nil
- while line = sock.gets
- case line
- when /^USER (.+)\r\n/
- user = $1
- if @users.key?(user)
- sock.print "+OK\r\n"
- else
- sock.print "-ERR unknown user\r\n"
- end
- when /^PASS (.+)\r\n/
- if @users[user] == $1
- sock.print "+OK\r\n"
- else
- sock.print "-ERR invalid password\r\n"
- end
- when /^APOP (.+) (.+)\r\n/
- user = $1
- if apop && Digest::MD5.hexdigest("<#{apop}>#{@users[user]}") == $2
- sock.print "+OK\r\n"
- else
- sock.print "-ERR authentication failed\r\n"
- end
- when /^QUIT/
- sock.print "+OK bye\r\n"
- return
- else
- sock.print "-ERR command not recognized\r\n"
- return
- end
- end
- end
-end
diff --git a/test/net/protocol/test_protocol.rb b/test/net/protocol/test_protocol.rb
index d3dc2ccf4c..2f42fa3236 100644
--- a/test/net/protocol/test_protocol.rb
+++ b/test/net/protocol/test_protocol.rb
@@ -57,6 +57,14 @@ class TestProtocol < Test::Unit::TestCase
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)
@@ -119,4 +127,33 @@ class TestProtocol < Test::Unit::TestCase
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 3cf909a762..0000000000
--- a/test/net/smtp/test_response.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-# frozen_string_literal: true
-require 'net/smtp'
-require 'test/unit'
-
-module Net
- class SMTP
- class TestResponse < Test::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 90c92e06f8..0000000000
--- a/test/net/smtp/test_smtp.rb
+++ /dev/null
@@ -1,200 +0,0 @@
-# frozen_string_literal: true
-require 'net/smtp'
-require 'stringio'
-require 'test/unit'
-
-module Net
- class TestSMTP < Test::Unit::TestCase
- CA_FILE = File.expand_path("../fixtures/cacert.pem", __dir__)
- SERVER_KEY = File.expand_path("../fixtures/server.key", __dir__)
- SERVER_CERT = File.expand_path("../fixtures/server.crt", __dir__)
-
- class FakeSocket
- attr_reader :write_io
-
- def initialize out = "250 OK\n"
- @write_io = StringIO.new
- @read_io = StringIO.new out
- end
-
- def writeline line
- @write_io.write "#{line}\r\n"
- end
-
- def readline
- line = @read_io.gets
- raise 'ran out of input' unless line
- line.chop
- end
- end
-
- def test_critical
- smtp = Net::SMTP.new 'localhost', 25
-
- assert_raise RuntimeError do
- smtp.send :critical do
- raise 'fail on purpose'
- end
- end
-
- assert_kind_of Net::SMTP::Response, smtp.send(:critical),
- '[Bug #9125]'
- end
-
- def test_esmtp
- smtp = Net::SMTP.new 'localhost', 25
- assert smtp.esmtp
- assert smtp.esmtp?
-
- smtp.esmtp = 'omg'
- assert_equal 'omg', smtp.esmtp
- assert_equal 'omg', smtp.esmtp?
- end
-
- def test_rset
- smtp = Net::SMTP.new 'localhost', 25
- smtp.instance_variable_set :@socket, FakeSocket.new
-
- assert smtp.rset
- end
-
- def test_mailfrom
- sock = FakeSocket.new
- smtp = Net::SMTP.new 'localhost', 25
- smtp.instance_variable_set :@socket, sock
- assert smtp.mailfrom("foo@example.com").success?
- assert_equal "MAIL FROM:<foo@example.com>\r\n", sock.write_io.string
- end
-
- def test_rcptto
- sock = FakeSocket.new
- smtp = Net::SMTP.new 'localhost', 25
- smtp.instance_variable_set :@socket, sock
- assert smtp.rcptto("foo@example.com").success?
- assert_equal "RCPT TO:<foo@example.com>\r\n", sock.write_io.string
- end
-
- def test_auth_plain
- sock = FakeSocket.new
- smtp = Net::SMTP.new 'localhost', 25
- smtp.instance_variable_set :@socket, sock
- assert smtp.auth_plain("foo", "bar").success?
- assert_equal "AUTH PLAIN AGZvbwBiYXI=\r\n", sock.write_io.string
- end
-
- def test_crlf_injection
- smtp = Net::SMTP.new 'localhost', 25
- smtp.instance_variable_set :@socket, FakeSocket.new
-
- assert_raise(ArgumentError) do
- smtp.mailfrom("foo\r\nbar")
- end
-
- assert_raise(ArgumentError) do
- smtp.mailfrom("foo\rbar")
- end
-
- assert_raise(ArgumentError) do
- smtp.mailfrom("foo\nbar")
- end
-
- assert_raise(ArgumentError) do
- smtp.rcptto("foo\r\nbar")
- end
- end
-
- def test_tls_connect
- servers = Socket.tcp_server_sockets("localhost", 0)
- 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)
- }
- begin
- sock = nil
- Thread.start do
- s = accept(servers)
- sock = OpenSSL::SSL::SSLSocket.new(s, ctx)
- sock.sync_close = true
- sock.accept
- sock.write("220 localhost Service ready\r\n")
- sock.gets
- sock.write("250 localhost\r\n")
- sock.gets
- sock.write("221 localhost Service closing transmission channel\r\n")
- end
- smtp = Net::SMTP.new("localhost", servers[0].local_address.ip_port)
- smtp.enable_tls
- smtp.open_timeout = 1
- smtp.start do
- end
- ensure
- sock.close if sock
- servers.each(&:close)
- end
- rescue LoadError
- # skip (require openssl)
- end
-
- def test_tls_connect_timeout
- servers = Socket.tcp_server_sockets("localhost", 0)
- begin
- sock = nil
- Thread.start do
- sock = accept(servers)
- end
- smtp = Net::SMTP.new("localhost", servers[0].local_address.ip_port)
- smtp.enable_tls
- smtp.open_timeout = 0.1
- assert_raise(Net::OpenTimeout) do
- smtp.start do
- end
- end
- rescue LoadError
- # skip (require openssl)
- ensure
- sock.close if sock
- servers.each(&:close)
- end
- end
-
- def test_eof_error_backtrace
- bug13018 = '[ruby-core:78550] [Bug #13018]'
- servers = Socket.tcp_server_sockets("localhost", 0)
- begin
- sock = nil
- t = Thread.start do
- sock = accept(servers)
- sock.close
- end
- smtp = Net::SMTP.new("localhost", servers[0].local_address.ip_port)
- e = assert_raise(EOFError, bug13018) do
- smtp.start do
- end
- end
- assert_equal(EOFError, e.class, bug13018)
- assert(e.backtrace.grep(%r"\bnet/smtp\.rb:").size > 0, bug13018)
- ensure
- sock.close if sock
- servers.each(&:close)
- t.join
- end
- end
-
- private
-
- def accept(servers)
- loop do
- readable, = IO.select(servers.map(&:to_io))
- readable.each do |r|
- sock, = r.accept_nonblock(exception: false)
- next if sock == :wait_readable
- return sock
- end
- end
- 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 342391f159..0000000000
--- a/test/net/smtp/test_ssl_socket.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-# frozen_string_literal: true
-require 'net/smtp'
-require 'test/unit'
-
-module Net
- class TestSSLSocket < Test::Unit::TestCase
- class MySMTP < SMTP
- attr_accessor :fake_tcp, :fake_ssl
-
- def initialize(*args)
- super(*args)
- @open_timeout = nil
- end
-
- 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_raise(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)