ssl_certifier 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.rdoc +19 -0
- data/Rakefile +6 -0
- data/certs/cacert.pem +3987 -0
- data/lib/ssl_certifier.rb +4 -0
- data/lib/ssl_certifier/open-uri.rb +12 -0
- data/lib/ssl_certifier/version.rb +3 -0
- data/spec/certs/ca_cert.pem +59 -0
- data/spec/certs/server_cert.pem +61 -0
- data/spec/certs/server_key +67 -0
- data/spec/data.txt +1 -0
- data/spec/open-uri_spec.rb +697 -0
- data/spec/open-uri_ssl_spec.rb +89 -0
- data/spec/spec_helper.rb +73 -0
- data/ssl_certifier.gemspec +23 -0
- metadata +85 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module OpenURI
|
|
2
|
+
CaCertOptions = {:ssl_ca_cert => File.join(File.expand_path("../../../certs/", __FILE__), 'cacert.pem')}
|
|
3
|
+
|
|
4
|
+
class << self
|
|
5
|
+
alias_method :open_http_without_ca_cert, :open_http
|
|
6
|
+
|
|
7
|
+
def OpenURI.open_http(buf, target, proxy, options) # :nodoc:
|
|
8
|
+
options = CaCertOptions.merge(options)
|
|
9
|
+
OpenURI.open_http_without_ca_cert(buf, target, proxy, options)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Certificate:
|
|
2
|
+
Data:
|
|
3
|
+
Version: 3 (0x2)
|
|
4
|
+
Serial Number: 0 (0x0)
|
|
5
|
+
Signature Algorithm: sha1WithRSAEncryption
|
|
6
|
+
Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA
|
|
7
|
+
Validity
|
|
8
|
+
Not Before: Jan 1 00:00:00 2009 GMT
|
|
9
|
+
Not After : Dec 31 23:59:59 2049 GMT
|
|
10
|
+
Subject: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA
|
|
11
|
+
Subject Public Key Info:
|
|
12
|
+
Public Key Algorithm: rsaEncryption
|
|
13
|
+
RSA Public Key: (1024 bit)
|
|
14
|
+
Modulus (1024 bit):
|
|
15
|
+
00:9f:58:19:39:bc:ea:0c:b8:c3:5d:12:a7:d8:20:
|
|
16
|
+
6c:53:ac:91:34:c8:b4:db:3f:56:f6:75:b6:6c:23:
|
|
17
|
+
80:23:6a:5f:b3:f6:9a:3e:00:b4:16:19:1c:9c:2c:
|
|
18
|
+
8d:e8:53:d5:0b:f1:52:3f:7b:60:93:86:ae:89:ab:
|
|
19
|
+
20:82:9a:b6:72:14:3c:4d:a9:0b:6c:34:79:9e:d3:
|
|
20
|
+
14:82:6d:c9:3b:90:d9:5e:68:6f:8c:b5:d8:09:f4:
|
|
21
|
+
6f:3b:22:9f:5e:81:9c:37:df:cf:90:36:65:57:dc:
|
|
22
|
+
ad:31:ca:8b:48:92:a7:3c:1e:42:e9:1c:4e:1e:cb:
|
|
23
|
+
36:c1:44:4e:ab:9a:b2:73:6d
|
|
24
|
+
Exponent: 65537 (0x10001)
|
|
25
|
+
X509v3 extensions:
|
|
26
|
+
X509v3 Basic Constraints:
|
|
27
|
+
CA:FALSE
|
|
28
|
+
Netscape Comment:
|
|
29
|
+
OpenSSL Generated Certificate
|
|
30
|
+
X509v3 Subject Key Identifier:
|
|
31
|
+
24:6F:03:A3:EE:06:51:75:B2:BA:FC:3A:38:59:BF:ED:87:CD:E8:7F
|
|
32
|
+
X509v3 Authority Key Identifier:
|
|
33
|
+
keyid:24:6F:03:A3:EE:06:51:75:B2:BA:FC:3A:38:59:BF:ED:87:CD:E8:7F
|
|
34
|
+
|
|
35
|
+
Signature Algorithm: sha1WithRSAEncryption
|
|
36
|
+
13:eb:db:ca:cd:90:f2:09:9e:d9:72:70:5e:42:5d:11:84:ce:
|
|
37
|
+
00:1d:c4:2f:41:d2:3e:16:e5:d4:97:1f:43:a9:a7:9c:fa:60:
|
|
38
|
+
c4:35:96:f2:f6:0d:13:6d:0f:36:dd:59:03:08:ee:2e:a6:df:
|
|
39
|
+
9e:d8:6d:ca:72:8f:02:c2:2b:53:7b:12:7f:55:81:6c:9e:7d:
|
|
40
|
+
e7:40:7e:f8:f5:75:0d:4b:a0:8d:ee:a4:d9:e8:5f:06:c9:86:
|
|
41
|
+
66:71:70:6c:41:81:6a:dd:a4:4f:a3:c1:ac:70:d4:78:1b:23:
|
|
42
|
+
30:2f:a5:ef:98:ee:d4:62:80:fd:bf:d4:7a:9b:8e:2d:18:e5:
|
|
43
|
+
00:46
|
|
44
|
+
-----BEGIN CERTIFICATE-----
|
|
45
|
+
MIICfzCCAeigAwIBAgIBADANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJKUDEO
|
|
46
|
+
MAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRUwEwYDVQQDEwxSdWJ5
|
|
47
|
+
IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBHMQswCQYD
|
|
48
|
+
VQQGEwJKUDEOMAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRUwEwYD
|
|
49
|
+
VQQDEwxSdWJ5IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ9Y
|
|
50
|
+
GTm86gy4w10Sp9ggbFOskTTItNs/VvZ1tmwjgCNqX7P2mj4AtBYZHJwsjehT1Qvx
|
|
51
|
+
Uj97YJOGromrIIKatnIUPE2pC2w0eZ7TFIJtyTuQ2V5ob4y12An0bzsin16BnDff
|
|
52
|
+
z5A2ZVfcrTHKi0iSpzweQukcTh7LNsFETquasnNtAgMBAAGjezB5MAkGA1UdEwQC
|
|
53
|
+
MAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRl
|
|
54
|
+
MB0GA1UdDgQWBBQkbwOj7gZRdbK6/Do4Wb/th83ofzAfBgNVHSMEGDAWgBQkbwOj
|
|
55
|
+
7gZRdbK6/Do4Wb/th83ofzANBgkqhkiG9w0BAQUFAAOBgQAT69vKzZDyCZ7ZcnBe
|
|
56
|
+
Ql0RhM4AHcQvQdI+FuXUlx9Dqaec+mDENZby9g0TbQ823VkDCO4upt+e2G3Kco8C
|
|
57
|
+
witTexJ/VYFsnn3nQH749XUNS6CN7qTZ6F8GyYZmcXBsQYFq3aRPo8GscNR4GyMw
|
|
58
|
+
L6XvmO7UYoD9v9R6m44tGOUARg==
|
|
59
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Certificate:
|
|
2
|
+
Data:
|
|
3
|
+
Version: 3 (0x2)
|
|
4
|
+
Serial Number: 1 (0x1)
|
|
5
|
+
Signature Algorithm: sha1WithRSAEncryption
|
|
6
|
+
Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA
|
|
7
|
+
Validity
|
|
8
|
+
Not Before: Jan 1 00:00:00 2009 GMT
|
|
9
|
+
Not After : Dec 31 23:59:59 2049 GMT
|
|
10
|
+
Subject: C=JP, ST=Tokyo, O=RubyTest, CN=127.0.0.1
|
|
11
|
+
Subject Public Key Info:
|
|
12
|
+
Public Key Algorithm: rsaEncryption
|
|
13
|
+
RSA Public Key: (1024 bit)
|
|
14
|
+
Modulus (1024 bit):
|
|
15
|
+
00:bb:bd:74:69:53:58:50:24:79:f2:eb:db:8b:97:
|
|
16
|
+
e4:69:a4:dd:48:0c:40:35:62:42:b3:35:8c:96:2a:
|
|
17
|
+
62:76:98:b5:2a:e0:f8:78:33:b6:ff:f8:55:bf:44:
|
|
18
|
+
69:21:d7:b5:0e:bd:8a:dd:31:1b:88:d5:b4:5e:7a:
|
|
19
|
+
82:e0:ba:99:6c:04:76:e9:ff:e6:f8:f5:06:8e:7e:
|
|
20
|
+
a4:db:db:eb:43:44:12:a7:ca:ca:2b:aa:5f:83:10:
|
|
21
|
+
e2:9e:35:55:e8:e8:af:be:c8:7d:bb:c2:d4:aa:c1:
|
|
22
|
+
1c:57:0b:c0:0c:3a:1d:6e:23:a9:03:26:7c:ea:8c:
|
|
23
|
+
f0:86:61:ce:f1:ff:42:c7:23
|
|
24
|
+
Exponent: 65537 (0x10001)
|
|
25
|
+
X509v3 extensions:
|
|
26
|
+
X509v3 Basic Constraints:
|
|
27
|
+
CA:FALSE
|
|
28
|
+
Netscape Cert Type:
|
|
29
|
+
SSL Server
|
|
30
|
+
Netscape Comment:
|
|
31
|
+
OpenSSL Generated Certificate
|
|
32
|
+
X509v3 Subject Key Identifier:
|
|
33
|
+
7F:17:5A:58:88:96:E1:1F:44:EA:FF:AD:C6:2E:90:E2:95:32:DD:F0
|
|
34
|
+
X509v3 Authority Key Identifier:
|
|
35
|
+
keyid:24:6F:03:A3:EE:06:51:75:B2:BA:FC:3A:38:59:BF:ED:87:CD:E8:7F
|
|
36
|
+
|
|
37
|
+
Signature Algorithm: sha1WithRSAEncryption
|
|
38
|
+
9a:34:99:ea:76:a2:ed:f0:f7:a7:75:3b:81:fb:75:57:93:c1:
|
|
39
|
+
27:b6:1e:7a:38:67:95:be:58:42:9a:0a:dd:2b:23:fb:85:42:
|
|
40
|
+
80:34:bf:b9:0e:9c:5e:5a:dc:2d:25:8c:68:02:a2:c7:7f:c0:
|
|
41
|
+
eb:f3:e0:61:e2:05:e5:7e:c1:e0:33:1c:76:65:23:2c:25:08:
|
|
42
|
+
f6:5a:11:b9:d4:f7:e3:80:bb:b0:ce:76:1a:56:22:af:e2:4a:
|
|
43
|
+
e1:7e:a4:60:f3:fd:9c:53:46:51:57:32:6b:05:53:80:5c:a5:
|
|
44
|
+
61:93:87:ae:06:a8:a2:ba:4d:a1:b7:1b:0f:8f:82:0a:e8:b3:
|
|
45
|
+
ea:63
|
|
46
|
+
-----BEGIN CERTIFICATE-----
|
|
47
|
+
MIICkTCCAfqgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJKUDEO
|
|
48
|
+
MAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRUwEwYDVQQDEwxSdWJ5
|
|
49
|
+
IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBEMQswCQYD
|
|
50
|
+
VQQGEwJKUDEOMAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRIwEAYD
|
|
51
|
+
VQQDEwkxMjcuMC4wLjEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALu9dGlT
|
|
52
|
+
WFAkefLr24uX5Gmk3UgMQDViQrM1jJYqYnaYtSrg+Hgztv/4Vb9EaSHXtQ69it0x
|
|
53
|
+
G4jVtF56guC6mWwEdun/5vj1Bo5+pNvb60NEEqfKyiuqX4MQ4p41Vejor77IfbvC
|
|
54
|
+
1KrBHFcLwAw6HW4jqQMmfOqM8IZhzvH/QscjAgMBAAGjgY8wgYwwCQYDVR0TBAIw
|
|
55
|
+
ADARBglghkgBhvhCAQEEBAMCBkAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu
|
|
56
|
+
ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBR/F1pYiJbhH0Tq/63GLpDilTLd
|
|
57
|
+
8DAfBgNVHSMEGDAWgBQkbwOj7gZRdbK6/Do4Wb/th83ofzANBgkqhkiG9w0BAQUF
|
|
58
|
+
AAOBgQCaNJnqdqLt8PendTuB+3VXk8Enth56OGeVvlhCmgrdKyP7hUKANL+5Dpxe
|
|
59
|
+
WtwtJYxoAqLHf8Dr8+Bh4gXlfsHgMxx2ZSMsJQj2WhG51PfjgLuwznYaViKv4krh
|
|
60
|
+
fqRg8/2cU0ZRVzJrBVOAXKVhk4euBqiiuk2htxsPj4IK6LPqYw==
|
|
61
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Private-Key: (1024 bit)
|
|
2
|
+
modulus:
|
|
3
|
+
00:bb:bd:74:69:53:58:50:24:79:f2:eb:db:8b:97:
|
|
4
|
+
e4:69:a4:dd:48:0c:40:35:62:42:b3:35:8c:96:2a:
|
|
5
|
+
62:76:98:b5:2a:e0:f8:78:33:b6:ff:f8:55:bf:44:
|
|
6
|
+
69:21:d7:b5:0e:bd:8a:dd:31:1b:88:d5:b4:5e:7a:
|
|
7
|
+
82:e0:ba:99:6c:04:76:e9:ff:e6:f8:f5:06:8e:7e:
|
|
8
|
+
a4:db:db:eb:43:44:12:a7:ca:ca:2b:aa:5f:83:10:
|
|
9
|
+
e2:9e:35:55:e8:e8:af:be:c8:7d:bb:c2:d4:aa:c1:
|
|
10
|
+
1c:57:0b:c0:0c:3a:1d:6e:23:a9:03:26:7c:ea:8c:
|
|
11
|
+
f0:86:61:ce:f1:ff:42:c7:23
|
|
12
|
+
publicExponent: 65537 (0x10001)
|
|
13
|
+
privateExponent:
|
|
14
|
+
00:af:3a:ec:17:0a:f5:d9:07:d2:d3:4c:15:c5:3b:
|
|
15
|
+
66:b4:bc:6e:d5:ba:a9:8b:aa:45:3b:63:f5:ee:8b:
|
|
16
|
+
6d:0f:e9:04:e0:1a:cf:8f:d2:25:32:d1:a5:a7:3a:
|
|
17
|
+
c1:2e:17:5a:25:82:00:c4:e7:fb:1d:42:ea:71:6c:
|
|
18
|
+
c4:0f:e1:db:23:ff:1e:d6:c8:d6:60:ca:2d:06:fc:
|
|
19
|
+
54:3c:03:d4:09:96:bb:38:7a:22:a1:61:2c:f7:d0:
|
|
20
|
+
d0:90:6c:9f:61:ba:61:30:5a:aa:64:ad:43:3a:53:
|
|
21
|
+
38:e8:ba:cc:8c:51:3e:68:3e:3a:6a:0f:5d:5d:e0:
|
|
22
|
+
d6:df:f2:54:93:d3:14:22:a1
|
|
23
|
+
prime1:
|
|
24
|
+
00:e8:ec:11:fe:e6:2b:23:21:29:d5:40:a6:11:ec:
|
|
25
|
+
4c:ae:4d:08:2a:71:18:ac:d1:3e:40:2f:12:41:59:
|
|
26
|
+
12:09:e2:f7:c2:d7:6b:0a:96:0a:06:e3:90:6a:4e:
|
|
27
|
+
b2:eb:25:b7:09:68:e9:13:ab:d0:5a:29:7a:e4:72:
|
|
28
|
+
1a:ee:46:a0:8b
|
|
29
|
+
prime2:
|
|
30
|
+
00:ce:57:5e:31:e9:c9:a8:5b:1f:55:af:67:e2:49:
|
|
31
|
+
2a:af:90:b6:02:c0:32:2f:ca:ae:1e:de:47:81:73:
|
|
32
|
+
a8:f8:37:53:70:93:24:62:77:d4:b8:80:30:9f:65:
|
|
33
|
+
26:20:46:ae:5a:65:6e:6d:af:68:4c:8d:e8:3c:f3:
|
|
34
|
+
d1:d1:d9:6e:c9
|
|
35
|
+
exponent1:
|
|
36
|
+
03:f1:02:b8:f2:82:26:5d:08:4d:30:83:de:e7:c5:
|
|
37
|
+
c0:69:53:4b:0c:90:e3:53:c3:1e:e8:ed:01:28:15:
|
|
38
|
+
b3:0f:21:2c:2d:e3:04:d1:d7:27:98:b0:37:ec:4f:
|
|
39
|
+
00:c5:a9:9c:42:27:37:8a:ff:c2:96:d3:1a:8c:87:
|
|
40
|
+
c2:22:75:d3
|
|
41
|
+
exponent2:
|
|
42
|
+
6f:17:32:ab:84:c7:01:51:2d:e9:9f:ea:3a:36:52:
|
|
43
|
+
38:fb:9c:42:96:df:6e:43:9c:c3:19:c1:3d:bc:db:
|
|
44
|
+
77:e7:b1:90:a6:67:ac:6b:ff:a6:e5:bd:47:d3:d9:
|
|
45
|
+
56:ff:36:d7:8c:4c:8b:d9:28:3a:2f:1c:9d:d4:57:
|
|
46
|
+
5e:b7:c5:a1
|
|
47
|
+
coefficient:
|
|
48
|
+
45:50:47:66:56:e9:21:d9:40:0e:af:3f:f2:05:77:
|
|
49
|
+
ab:e7:08:40:97:88:2a:51:b3:7e:86:b0:b2:03:2e:
|
|
50
|
+
6d:36:3f:46:42:97:7d:5a:a2:93:6c:05:c2:8b:8b:
|
|
51
|
+
2d:af:d5:7d:75:e9:70:f0:2d:21:e3:b9:cf:4d:9a:
|
|
52
|
+
c4:97:e2:79
|
|
53
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
54
|
+
MIICXAIBAAKBgQC7vXRpU1hQJHny69uLl+RppN1IDEA1YkKzNYyWKmJ2mLUq4Ph4
|
|
55
|
+
M7b/+FW/RGkh17UOvYrdMRuI1bReeoLguplsBHbp/+b49QaOfqTb2+tDRBKnysor
|
|
56
|
+
ql+DEOKeNVXo6K++yH27wtSqwRxXC8AMOh1uI6kDJnzqjPCGYc7x/0LHIwIDAQAB
|
|
57
|
+
AoGBAK867BcK9dkH0tNMFcU7ZrS8btW6qYuqRTtj9e6LbQ/pBOAaz4/SJTLRpac6
|
|
58
|
+
wS4XWiWCAMTn+x1C6nFsxA/h2yP/HtbI1mDKLQb8VDwD1AmWuzh6IqFhLPfQ0JBs
|
|
59
|
+
n2G6YTBaqmStQzpTOOi6zIxRPmg+OmoPXV3g1t/yVJPTFCKhAkEA6OwR/uYrIyEp
|
|
60
|
+
1UCmEexMrk0IKnEYrNE+QC8SQVkSCeL3wtdrCpYKBuOQak6y6yW3CWjpE6vQWil6
|
|
61
|
+
5HIa7kagiwJBAM5XXjHpyahbH1WvZ+JJKq+QtgLAMi/Krh7eR4FzqPg3U3CTJGJ3
|
|
62
|
+
1LiAMJ9lJiBGrlplbm2vaEyN6Dzz0dHZbskCQAPxArjygiZdCE0wg97nxcBpU0sM
|
|
63
|
+
kONTwx7o7QEoFbMPISwt4wTR1yeYsDfsTwDFqZxCJzeK/8KW0xqMh8IiddMCQG8X
|
|
64
|
+
MquExwFRLemf6jo2Ujj7nEKW325DnMMZwT2823fnsZCmZ6xr/6blvUfT2Vb/NteM
|
|
65
|
+
TIvZKDovHJ3UV163xaECQEVQR2ZW6SHZQA6vP/IFd6vnCECXiCpRs36GsLIDLm02
|
|
66
|
+
P0ZCl31aopNsBcKLiy2v1X116XDwLSHjuc9NmsSX4nk=
|
|
67
|
+
-----END RSA PRIVATE KEY-----
|
data/spec/data.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ddd
|
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe OpenURI do
|
|
4
|
+
context "normal http operations" do
|
|
5
|
+
before(:each) do
|
|
6
|
+
@proxies = %w[http_proxy HTTP_PROXY ftp_proxy FTP_PROXY no_proxy]
|
|
7
|
+
@old_proxies = @proxies.map {|k| ENV[k] }
|
|
8
|
+
@proxies.each {|k| ENV[k] = nil }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
after(:each) do
|
|
12
|
+
@proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should return 200' do
|
|
16
|
+
with_http do |srv, dr, url|
|
|
17
|
+
open("#{dr}/foo200", "w") {|f| f << "foo200" }
|
|
18
|
+
open("#{url}/foo200") do |f|
|
|
19
|
+
f.status[0].should == "200"
|
|
20
|
+
f.read.should == "foo200"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should return 200 for a big file' do
|
|
26
|
+
with_http do |srv, dr, url|
|
|
27
|
+
content = "foo200big"*10240
|
|
28
|
+
open("#{dr}/foo200big", "w") {|f| f << content }
|
|
29
|
+
open("#{url}/foo200big") do |f|
|
|
30
|
+
f.status[0].should == "200"
|
|
31
|
+
f.read.should == content
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'should return 404 for a bad url' do
|
|
37
|
+
with_http do |srv, dr, url|
|
|
38
|
+
lambda { open("#{url}/not-exist") {} }.should raise_error(OpenURI::HTTPError)
|
|
39
|
+
#TODO: somehow get the status from the lambda
|
|
40
|
+
#exc.io.status[0].should == "404"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should read the string "foo_ou"' do
|
|
45
|
+
with_http do |srv, dr, url|
|
|
46
|
+
open("#{dr}/foo_ou", "w") {|f| f << "foo_ou" }
|
|
47
|
+
u = URI("#{url}/foo_ou")
|
|
48
|
+
open(u) do |f|
|
|
49
|
+
f.status[0].should == "200"
|
|
50
|
+
f.read.should == "foo_ou"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'should raise an ArgumentError when open is given too many args' do
|
|
56
|
+
lambda { open("http://192.0.2.1/tma", "r", 0666, :extra) {} }.should raise_error(ArgumentError)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'should raise an ArgumentError when open is given an invalid argument' do
|
|
60
|
+
lambda { open("http://127.0.0.1/", :invalid_option=>true) {} }.should raise_error(ArgumentError)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'should raise a Timeout::Error when a read times out' do
|
|
64
|
+
TCPServer.open("127.0.0.1", 0) do |serv|
|
|
65
|
+
port = serv.addr[1]
|
|
66
|
+
th = Thread.new {
|
|
67
|
+
sock = serv.accept
|
|
68
|
+
begin
|
|
69
|
+
req = sock.gets("\r\n\r\n")
|
|
70
|
+
req.should match %r{\AGET /foo/bar }
|
|
71
|
+
sock.print "HTTP/1.0 200 OK\r\n"
|
|
72
|
+
sock.print "Content-Length: 4\r\n\r\n"
|
|
73
|
+
sleep 1
|
|
74
|
+
sock.print "ab\r\n"
|
|
75
|
+
ensure
|
|
76
|
+
sock.close
|
|
77
|
+
end
|
|
78
|
+
}
|
|
79
|
+
begin
|
|
80
|
+
lambda { URI("http://127.0.0.1:#{port}/foo/bar").read(:read_timeout=>0.01) }.should raise_error(Timeout::Error)
|
|
81
|
+
ensure
|
|
82
|
+
Thread.kill(th)
|
|
83
|
+
th.join
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'should read when opening in various modes' do
|
|
89
|
+
with_http do |srv, dr, url|
|
|
90
|
+
open("#{dr}/mode", "w") {|f| f << "mode" }
|
|
91
|
+
open("#{url}/mode", "r") do |f|
|
|
92
|
+
f.status[0].should == "200"
|
|
93
|
+
f.read.should == "mode"
|
|
94
|
+
end
|
|
95
|
+
open("#{url}/mode", "r", 0600) do |f|
|
|
96
|
+
f.status[0].should == "200"
|
|
97
|
+
f.read.should == "mode"
|
|
98
|
+
end
|
|
99
|
+
lambda { open("#{url}/mode", "a") {} }.should raise_error(ArgumentError)
|
|
100
|
+
open("#{url}/mode", "r:us-ascii") do |f|
|
|
101
|
+
f.read.encoding.should == Encoding::US_ASCII
|
|
102
|
+
end
|
|
103
|
+
open("#{url}/mode", "r:utf-8") do |f|
|
|
104
|
+
f.read.encoding.should == Encoding::UTF_8
|
|
105
|
+
end
|
|
106
|
+
lambda { open("#{url}/mode", "r:invalid-encoding") {} }.should raise_error(ArgumentError)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'should open a url when a block is not specified' do
|
|
111
|
+
with_http do |srv, dr, url|
|
|
112
|
+
open("#{dr}/without_block", "w") {|g| g << "without_block" }
|
|
113
|
+
begin
|
|
114
|
+
f = open("#{url}/without_block")
|
|
115
|
+
f.status[0].should == "200"
|
|
116
|
+
f.read.should == "without_block"
|
|
117
|
+
ensure
|
|
118
|
+
f.close
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'should read the same headers for header1 and header2' do
|
|
124
|
+
myheader1 = 'barrrr'
|
|
125
|
+
myheader2 = nil
|
|
126
|
+
with_http do |srv, dr, url|
|
|
127
|
+
srv.mount_proc("/h/") {|req, res| myheader2 = req['myheader']; res.body = "foo" }
|
|
128
|
+
open("#{url}/h/", 'MyHeader'=>myheader1) do |f|
|
|
129
|
+
f.read.should == "foo"
|
|
130
|
+
myheader1.should == myheader2
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'should not open with multiple proxy options' do
|
|
136
|
+
lambda { open("http://127.0.0.1/", :proxy_http_basic_authentication=>true, :proxy=>true) {} }.should raise_error(ArgumentError)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'should not open with a non-http proxy' do
|
|
140
|
+
lambda { open("http://127.0.0.1/", :proxy=>URI("ftp://127.0.0.1/")) {} }.should raise_error(RuntimeError)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'should open a url via a proxy' do
|
|
144
|
+
with_http do |srv, dr, url|
|
|
145
|
+
log = ''
|
|
146
|
+
proxy = WEBrick::HTTPProxyServer.new({
|
|
147
|
+
:ServerType => Thread,
|
|
148
|
+
:Logger => WEBrick::Log.new(NullLog),
|
|
149
|
+
:AccessLog => [[NullLog, ""]],
|
|
150
|
+
:ProxyAuthProc => lambda {|req, res|
|
|
151
|
+
log << req.request_line
|
|
152
|
+
},
|
|
153
|
+
:BindAddress => '127.0.0.1',
|
|
154
|
+
:Port => 0})
|
|
155
|
+
_, proxy_port, _, proxy_host = proxy.listeners[0].addr
|
|
156
|
+
proxy_url = "http://#{proxy_host}:#{proxy_port}/"
|
|
157
|
+
begin
|
|
158
|
+
th = proxy.start
|
|
159
|
+
open("#{dr}/proxy", "w") {|f| f << "proxy" }
|
|
160
|
+
open("#{url}/proxy", :proxy=>proxy_url) do |f|
|
|
161
|
+
f.status[0].should == "200"
|
|
162
|
+
f.read.should == "proxy"
|
|
163
|
+
end
|
|
164
|
+
log.should match /#{Regexp.quote url}/
|
|
165
|
+
log.clear
|
|
166
|
+
|
|
167
|
+
open("#{url}/proxy", :proxy=>URI(proxy_url)) do |f|
|
|
168
|
+
f.status[0].should == "200"
|
|
169
|
+
f.read.should == "proxy"
|
|
170
|
+
end
|
|
171
|
+
log.should match /#{Regexp.quote url}/
|
|
172
|
+
log.clear
|
|
173
|
+
|
|
174
|
+
open("#{url}/proxy", :proxy=>nil) do |f|
|
|
175
|
+
f.status[0].should == "200"
|
|
176
|
+
f.read.should == "proxy"
|
|
177
|
+
end
|
|
178
|
+
log.should == ""
|
|
179
|
+
log.clear
|
|
180
|
+
|
|
181
|
+
lambda { open("#{url}/proxy", :proxy=>:invalid) {} }.should raise_error(ArgumentError)
|
|
182
|
+
log.should == ""
|
|
183
|
+
log.clear
|
|
184
|
+
|
|
185
|
+
with_env("http_proxy"=>proxy_url) {
|
|
186
|
+
# should not use proxy for 127.0.0.0/8.
|
|
187
|
+
open("#{url}/proxy") do |f|
|
|
188
|
+
f.status[0].should == "200"
|
|
189
|
+
f.read.should == "proxy"
|
|
190
|
+
end
|
|
191
|
+
}
|
|
192
|
+
log.should == ""
|
|
193
|
+
log.clear
|
|
194
|
+
ensure
|
|
195
|
+
proxy.shutdown
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it 'should open a url via proxy with http basic authentication' do
|
|
201
|
+
with_http do |srv, dr, url|
|
|
202
|
+
log = ''
|
|
203
|
+
proxy = WEBrick::HTTPProxyServer.new({
|
|
204
|
+
:ServerType => Thread,
|
|
205
|
+
:Logger => WEBrick::Log.new(NullLog),
|
|
206
|
+
:AccessLog => [[NullLog, ""]],
|
|
207
|
+
:ProxyAuthProc => lambda {|req, res|
|
|
208
|
+
log << req.request_line
|
|
209
|
+
if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}"
|
|
210
|
+
raise WEBrick::HTTPStatus::ProxyAuthenticationRequired
|
|
211
|
+
end
|
|
212
|
+
},
|
|
213
|
+
:BindAddress => '127.0.0.1',
|
|
214
|
+
:Port => 0})
|
|
215
|
+
_, proxy_port, _, proxy_host = proxy.listeners[0].addr
|
|
216
|
+
proxy_url = "http://#{proxy_host}:#{proxy_port}/"
|
|
217
|
+
begin
|
|
218
|
+
th = proxy.start
|
|
219
|
+
open("#{dr}/proxy", "w") {|f| f << "proxy" }
|
|
220
|
+
lambda { open("#{url}/proxy", :proxy=>proxy_url) {} }.should raise_error(OpenURI::HTTPError)
|
|
221
|
+
#TODO: somehow extract the status from the lambda
|
|
222
|
+
#exc.io.status[0].should == "407"
|
|
223
|
+
log.should match /#{Regexp.quote url}/
|
|
224
|
+
log.clear
|
|
225
|
+
|
|
226
|
+
open("#{url}/proxy", :proxy_http_basic_authentication=>[proxy_url, "user", "pass"]) do |f|
|
|
227
|
+
f.status[0].should == "200"
|
|
228
|
+
f.read.should == "proxy"
|
|
229
|
+
end
|
|
230
|
+
log.should match /#{Regexp.quote url}/
|
|
231
|
+
log.clear
|
|
232
|
+
|
|
233
|
+
lambda { open("#{url}/proxy", :proxy_http_basic_authentication=>[true, "user", "pass"]) {} }.should raise_error(ArgumentError)
|
|
234
|
+
log.should == ""
|
|
235
|
+
log.clear
|
|
236
|
+
ensure
|
|
237
|
+
proxy.shutdown
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it 'should open urls with redirects' do
|
|
243
|
+
with_http do |srv, dr, url|
|
|
244
|
+
srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" }
|
|
245
|
+
srv.mount_proc("/r2/") {|req, res| res.body = "r2" }
|
|
246
|
+
srv.mount_proc("/to-file/") {|req, res| res.status = 301; res["location"] = "file:///foo" }
|
|
247
|
+
open("#{url}/r1/") do |f|
|
|
248
|
+
f.base_uri.to_s.should == "#{url}/r2"
|
|
249
|
+
f.read.should == "r2"
|
|
250
|
+
end
|
|
251
|
+
lambda { open("#{url}/r1/", :redirect=>false) {} }.should raise_error(OpenURI::HTTPRedirect)
|
|
252
|
+
lambda { open("#{url}/to-file/") {} }.should raise_error(RuntimeError)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it 'should raise a RuntimeError if it enters a redirect loop' do
|
|
257
|
+
with_http do |srv, dr, url|
|
|
258
|
+
srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" }
|
|
259
|
+
srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r1"; res.body = "r2" }
|
|
260
|
+
lambda { open("#{url}/r1/") {} }.should raise_error(RuntimeError)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
it 'should open a URI through a relative redirect' do
|
|
265
|
+
TCPServer.open("127.0.0.1", 0) do |serv|
|
|
266
|
+
port = serv.addr[1]
|
|
267
|
+
th = Thread.new {
|
|
268
|
+
sock = serv.accept
|
|
269
|
+
begin
|
|
270
|
+
req = sock.gets("\r\n\r\n")
|
|
271
|
+
req.should match %r{\AGET /foo/bar }
|
|
272
|
+
sock.print "HTTP/1.0 302 Found\r\n"
|
|
273
|
+
sock.print "Location: ../baz\r\n\r\n"
|
|
274
|
+
ensure
|
|
275
|
+
sock.close
|
|
276
|
+
end
|
|
277
|
+
sock = serv.accept
|
|
278
|
+
begin
|
|
279
|
+
req = sock.gets("\r\n\r\n")
|
|
280
|
+
req.should match %r{\AGET /baz }
|
|
281
|
+
sock.print "HTTP/1.0 200 OK\r\n"
|
|
282
|
+
sock.print "Content-Length: 4\r\n\r\n"
|
|
283
|
+
sock.print "ab\r\n"
|
|
284
|
+
ensure
|
|
285
|
+
sock.close
|
|
286
|
+
end
|
|
287
|
+
}
|
|
288
|
+
begin
|
|
289
|
+
content = URI("http://127.0.0.1:#{port}/foo/bar").read
|
|
290
|
+
content.should == "ab\r\n"
|
|
291
|
+
ensure
|
|
292
|
+
Thread.kill(th)
|
|
293
|
+
th.join
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
it 'should raise a OpenURI::HTTPError if an invalid redirect is encountered' do
|
|
299
|
+
TCPServer.open("127.0.0.1", 0) do |serv|
|
|
300
|
+
port = serv.addr[1]
|
|
301
|
+
th = Thread.new {
|
|
302
|
+
sock = serv.accept
|
|
303
|
+
begin
|
|
304
|
+
req = sock.gets("\r\n\r\n")
|
|
305
|
+
req.should match %r{\AGET /foo/bar }
|
|
306
|
+
sock.print "HTTP/1.0 302 Found\r\n"
|
|
307
|
+
sock.print "Location: ::\r\n\r\n"
|
|
308
|
+
ensure
|
|
309
|
+
sock.close
|
|
310
|
+
end
|
|
311
|
+
}
|
|
312
|
+
begin
|
|
313
|
+
lambda { URI("http://127.0.0.1:#{port}/foo/bar").read }.should raise_error(OpenURI::HTTPError)
|
|
314
|
+
ensure
|
|
315
|
+
Thread.kill(th)
|
|
316
|
+
th.join
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it 'should open a url with basic authentication after being redirected' do
|
|
322
|
+
with_http do |srv, dr, url|
|
|
323
|
+
srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2" }
|
|
324
|
+
srv.mount_proc("/r2/") do |req, res|
|
|
325
|
+
if req["Authorization"] != "Basic #{['user:pass'].pack('m').chomp}"
|
|
326
|
+
raise WEBrick::HTTPStatus::Unauthorized
|
|
327
|
+
end
|
|
328
|
+
res.body = "r2"
|
|
329
|
+
end
|
|
330
|
+
lambda{ open("#{url}/r2/") {} }.should raise_error(OpenURI::HTTPError)
|
|
331
|
+
#TODO: somehow get the status from the lambda
|
|
332
|
+
#exc.io.status[0].should == "401"
|
|
333
|
+
open("#{url}/r2/", :http_basic_authentication=>['user', 'pass']) do |f|
|
|
334
|
+
f.read.should == "r2"
|
|
335
|
+
end
|
|
336
|
+
lambda { open("#{url}/r1/", :http_basic_authentication=>['user', 'pass']) {} }.should raise_error(OpenURI::HTTPError)
|
|
337
|
+
#TODO: somehow get the status from the lambda
|
|
338
|
+
#exc.io.status[0].should == "401"
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
it 'should raise an ArgumentError if user information is incorrectly specified in the url' do
|
|
343
|
+
if "1.9.0" <= RUBY_VERSION
|
|
344
|
+
lambda { open("http://user:pass@127.0.0.1/") {} }.should raise_error(ArgumentError)
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
it 'should open a url and report the progress' do
|
|
349
|
+
with_http do |srv, dr, url|
|
|
350
|
+
content = "a" * 100000
|
|
351
|
+
srv.mount_proc("/data/") {|req, res| res.body = content }
|
|
352
|
+
length = []
|
|
353
|
+
progress = []
|
|
354
|
+
open("#{url}/data/",
|
|
355
|
+
:content_length_proc => lambda {|n| length << n },
|
|
356
|
+
:progress_proc => lambda {|n| progress << n }
|
|
357
|
+
) {|f|
|
|
358
|
+
length.length.should == 1
|
|
359
|
+
length[0].should == content.length
|
|
360
|
+
progress.length.should be > 1
|
|
361
|
+
progress.should == progress.sort
|
|
362
|
+
progress[-1].should == content.length
|
|
363
|
+
f.read.should == content
|
|
364
|
+
}
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
it 'should open a url and report the progress when the response is chunked' do
|
|
369
|
+
with_http do |srv, dr, url|
|
|
370
|
+
content = "a" * 100000
|
|
371
|
+
srv.mount_proc("/data/") {|req, res| res.body = content; res.chunked = true }
|
|
372
|
+
length = []
|
|
373
|
+
progress = []
|
|
374
|
+
open("#{url}/data/",
|
|
375
|
+
:content_length_proc => lambda {|n| length << n },
|
|
376
|
+
:progress_proc => lambda {|n| progress << n }
|
|
377
|
+
) {|f|
|
|
378
|
+
length.length.should == 1
|
|
379
|
+
length[0].should be nil
|
|
380
|
+
progress.length.should be > 1
|
|
381
|
+
progress.should == progress.sort
|
|
382
|
+
progress[-1].should == content.length
|
|
383
|
+
f.read.should == content
|
|
384
|
+
}
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
it 'should open a url and read from it' do
|
|
389
|
+
with_http do |srv, dr, url|
|
|
390
|
+
open("#{dr}/uriread", "w") {|f| f << "uriread" }
|
|
391
|
+
data = URI("#{url}/uriread").read
|
|
392
|
+
data.status[0].should == "200"
|
|
393
|
+
data.should == "uriread"
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
it 'should work with different encodings' do
|
|
398
|
+
with_http do |srv, dr, url|
|
|
399
|
+
content_u8 = "\u3042"
|
|
400
|
+
content_ej = "\xa2\xa4".force_encoding("euc-jp")
|
|
401
|
+
srv.mount_proc("/u8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset=utf-8' }
|
|
402
|
+
srv.mount_proc("/ej/") {|req, res| res.body = content_ej; res['content-type'] = 'TEXT/PLAIN; charset=EUC-JP' }
|
|
403
|
+
srv.mount_proc("/nc/") {|req, res| res.body = "aa"; res['content-type'] = 'Text/Plain' }
|
|
404
|
+
open("#{url}/u8/") do |f|
|
|
405
|
+
f.read.should == content_u8
|
|
406
|
+
f.content_type.should == "text/plain"
|
|
407
|
+
f.charset.should == "utf-8"
|
|
408
|
+
end
|
|
409
|
+
open("#{url}/ej/") do |f|
|
|
410
|
+
f.read.should == content_ej
|
|
411
|
+
f.content_type.should == "text/plain"
|
|
412
|
+
f.charset.should == "euc-jp"
|
|
413
|
+
end
|
|
414
|
+
open("#{url}/nc/") do |f|
|
|
415
|
+
f.read.should == "aa"
|
|
416
|
+
f.content_type.should == "text/plain"
|
|
417
|
+
f.charset.should == "iso-8859-1"
|
|
418
|
+
f.charset { "unknown" }.should == "unknown"
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
it 'should open a url with quoted attribute values' do
|
|
424
|
+
with_http do |srv, dr, url|
|
|
425
|
+
content_u8 = "\u3042"
|
|
426
|
+
srv.mount_proc("/qu8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset="utf\-8"' }
|
|
427
|
+
open("#{url}/qu8/") do |f|
|
|
428
|
+
f.read.should == content_u8
|
|
429
|
+
f.content_type.should == "text/plain"
|
|
430
|
+
f.charset.should == "utf-8"
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
it 'should read the last modified date from a url' do
|
|
436
|
+
with_http do |srv, dr, url|
|
|
437
|
+
srv.mount_proc("/data/") {|req, res| res.body = "foo"; res['last-modified'] = 'Fri, 07 Aug 2009 06:05:04 GMT' }
|
|
438
|
+
open("#{url}/data/") do |f|
|
|
439
|
+
f.read.should == "foo"
|
|
440
|
+
f.last_modified.should == Time.utc(2009,8,7,6,5,4)
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
it 'should read data with various content encoding' do
|
|
446
|
+
with_http do |srv, dr, url|
|
|
447
|
+
content = "abc" * 10000
|
|
448
|
+
Zlib::GzipWriter.wrap(StringIO.new(content_gz="".force_encoding("ascii-8bit"))) {|z| z.write content }
|
|
449
|
+
srv.mount_proc("/data/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip' }
|
|
450
|
+
srv.mount_proc("/data2/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip'; res.chunked = true }
|
|
451
|
+
srv.mount_proc("/noce/") {|req, res| res.body = content_gz }
|
|
452
|
+
open("#{url}/data/") do |f|
|
|
453
|
+
f.content_encoding.should == ['gzip']
|
|
454
|
+
f.read.force_encoding("ascii-8bit").should == content_gz
|
|
455
|
+
end
|
|
456
|
+
open("#{url}/data2/") do |f|
|
|
457
|
+
f.content_encoding.should == ['gzip']
|
|
458
|
+
f.read.force_encoding("ascii-8bit").should == content_gz
|
|
459
|
+
end
|
|
460
|
+
open("#{url}/noce/") do |f|
|
|
461
|
+
f.content_encoding.should == []
|
|
462
|
+
f.read.force_encoding("ascii-8bit").should == content_gz
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# 192.0.2.0/24 is TEST-NET. [RFC3330]
|
|
468
|
+
|
|
469
|
+
it 'should find the proxy for a given url' do
|
|
470
|
+
URI("http://192.0.2.1/").find_proxy.should be nil
|
|
471
|
+
URI("ftp://192.0.2.1/").find_proxy.should be nil
|
|
472
|
+
with_env('http_proxy'=>'http://127.0.0.1:8080') {
|
|
473
|
+
URI("http://192.0.2.1/").find_proxy.should == URI('http://127.0.0.1:8080')
|
|
474
|
+
URI("ftp://192.0.2.1/").find_proxy.should be nil
|
|
475
|
+
}
|
|
476
|
+
with_env('ftp_proxy'=>'http://127.0.0.1:8080') {
|
|
477
|
+
URI("http://192.0.2.1/").find_proxy.should be nil
|
|
478
|
+
URI("ftp://192.0.2.1/").find_proxy.should == URI('http://127.0.0.1:8080')
|
|
479
|
+
}
|
|
480
|
+
with_env('REQUEST_METHOD'=>'GET') {
|
|
481
|
+
URI("http://192.0.2.1/").find_proxy.should be nil
|
|
482
|
+
}
|
|
483
|
+
with_env('CGI_HTTP_PROXY'=>'http://127.0.0.1:8080', 'REQUEST_METHOD'=>'GET') {
|
|
484
|
+
URI("http://192.0.2.1/").find_proxy.should == URI('http://127.0.0.1:8080')
|
|
485
|
+
}
|
|
486
|
+
with_env('http_proxy'=>'http://127.0.0.1:8080', 'no_proxy'=>'192.0.2.2') {
|
|
487
|
+
URI("http://192.0.2.1/").find_proxy.should == URI('http://127.0.0.1:8080')
|
|
488
|
+
URI("http://192.0.2.2/").find_proxy.should be nil
|
|
489
|
+
}
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
it 'should find a proxy for a given url with a case sensitive env' do
|
|
493
|
+
with_env('http_proxy'=>'http://127.0.0.1:8080', 'REQUEST_METHOD'=>'GET') {
|
|
494
|
+
URI("http://192.0.2.1/").find_proxy.should == URI('http://127.0.0.1:8080')
|
|
495
|
+
}
|
|
496
|
+
with_env('HTTP_PROXY'=>'http://127.0.0.1:8081', 'REQUEST_METHOD'=>'GET') {
|
|
497
|
+
URI("http://192.0.2.1/").find_proxy.should be nil
|
|
498
|
+
}
|
|
499
|
+
with_env('http_proxy'=>'http://127.0.0.1:8080', 'HTTP_PROXY'=>'http://127.0.0.1:8081', 'REQUEST_METHOD'=>'GET') {
|
|
500
|
+
URI("http://192.0.2.1/").find_proxy.should == URI('http://127.0.0.1:8080')
|
|
501
|
+
}
|
|
502
|
+
end unless RUBY_PLATFORM =~ /mswin|mingw/
|
|
503
|
+
|
|
504
|
+
it 'should raise exceptions on invalid ftp requests' do
|
|
505
|
+
lambda { URI("ftp://127.0.0.1/").read }.should raise_error(ArgumentError)
|
|
506
|
+
lambda { URI("ftp://127.0.0.1/a%0Db").read }.should raise_error(ArgumentError)
|
|
507
|
+
lambda { URI("ftp://127.0.0.1/a%0Ab").read }.should raise_error(ArgumentError)
|
|
508
|
+
lambda { URI("ftp://127.0.0.1/a%0Db/f").read }.should raise_error(ArgumentError)
|
|
509
|
+
lambda { URI("ftp://127.0.0.1/a%0Ab/f").read }.should raise_error(ArgumentError)
|
|
510
|
+
lambda { URI("ftp://127.0.0.1/d/f;type=x") }.should raise_error(URI::InvalidComponentError)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
it 'should perform FTP operations' do
|
|
514
|
+
TCPServer.open("127.0.0.1", 0) {|serv|
|
|
515
|
+
_, port, _, host = serv.addr
|
|
516
|
+
th = Thread.new {
|
|
517
|
+
s = serv.accept
|
|
518
|
+
begin
|
|
519
|
+
s.print "220 Test FTP Server\r\n"
|
|
520
|
+
s.gets.should == "USER anonymous\r\n"; s.print "331 name ok\r\n"
|
|
521
|
+
s.gets.should match /\APASS .*\r\n\z/; s.print "230 logged in\r\n"
|
|
522
|
+
s.gets.should == "TYPE I\r\n"; s.print "200 type set to I\r\n"
|
|
523
|
+
s.gets.should == "CWD foo\r\n"; s.print "250 CWD successful\r\n"
|
|
524
|
+
s.gets.should == "PASV\r\n"
|
|
525
|
+
TCPServer.open("127.0.0.1", 0) {|data_serv|
|
|
526
|
+
_, data_serv_port, _, data_serv_host = data_serv.addr
|
|
527
|
+
hi = data_serv_port >> 8
|
|
528
|
+
lo = data_serv_port & 0xff
|
|
529
|
+
s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n"
|
|
530
|
+
s.gets.should == "RETR bar\r\n"; s.print "150 file okay\r\n"
|
|
531
|
+
data_sock = data_serv.accept
|
|
532
|
+
begin
|
|
533
|
+
data_sock << "content"
|
|
534
|
+
ensure
|
|
535
|
+
data_sock.close
|
|
536
|
+
end
|
|
537
|
+
s.print "226 transfer complete\r\n"
|
|
538
|
+
s.gets.should be nil
|
|
539
|
+
}
|
|
540
|
+
ensure
|
|
541
|
+
s.close if s
|
|
542
|
+
end
|
|
543
|
+
}
|
|
544
|
+
begin
|
|
545
|
+
content = URI("ftp://#{host}:#{port}/foo/bar").read
|
|
546
|
+
content.should == "content"
|
|
547
|
+
ensure
|
|
548
|
+
Thread.kill(th)
|
|
549
|
+
th.join
|
|
550
|
+
end
|
|
551
|
+
}
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
it 'should perform active FTP operations' do
|
|
555
|
+
TCPServer.open("127.0.0.1", 0) do |serv|
|
|
556
|
+
_, port, _, host = serv.addr
|
|
557
|
+
th = Thread.new {
|
|
558
|
+
s = serv.accept
|
|
559
|
+
begin
|
|
560
|
+
content = "content"
|
|
561
|
+
s.print "220 Test FTP Server\r\n"
|
|
562
|
+
s.gets.should == "USER anonymous\r\n"; s.print "331 name ok\r\n"
|
|
563
|
+
s.gets.should match /\APASS .*\r\n\z/; s.print "230 logged in\r\n"
|
|
564
|
+
s.gets.should == "TYPE I\r\n"; s.print "200 type set to I\r\n"
|
|
565
|
+
s.gets.should == "CWD foo\r\n"; s.print "250 CWD successful\r\n"
|
|
566
|
+
m = s.gets.should match /\APORT 127,0,0,1,(\d+),(\d+)\r\n\z/
|
|
567
|
+
active_port = m[1].to_i << 8 | m[2].to_i
|
|
568
|
+
TCPSocket.open("127.0.0.1", active_port) do |data_sock|
|
|
569
|
+
s.print "200 data connection opened\r\n"
|
|
570
|
+
s.gets.should == "RETR bar\r\n"; s.print "150 file okay\r\n"
|
|
571
|
+
begin
|
|
572
|
+
data_sock << content
|
|
573
|
+
ensure
|
|
574
|
+
data_sock.close
|
|
575
|
+
end
|
|
576
|
+
s.print "226 transfer complete\r\n"
|
|
577
|
+
s.gets.should be nil
|
|
578
|
+
end
|
|
579
|
+
ensure
|
|
580
|
+
s.close if s
|
|
581
|
+
end
|
|
582
|
+
}
|
|
583
|
+
begin
|
|
584
|
+
content = URI("ftp://#{host}:#{port}/foo/bar").read(:ftp_active_mode=>true)
|
|
585
|
+
content.should == "content"
|
|
586
|
+
ensure
|
|
587
|
+
Thread.kill(th)
|
|
588
|
+
th.join
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
it 'should perform FTP operations in ascii' do
|
|
594
|
+
TCPServer.open("127.0.0.1", 0) do |serv|
|
|
595
|
+
_, port, _, host = serv.addr
|
|
596
|
+
th = Thread.new {
|
|
597
|
+
s = serv.accept
|
|
598
|
+
begin
|
|
599
|
+
content = "content"
|
|
600
|
+
s.print "220 Test FTP Server\r\n"
|
|
601
|
+
s.gets.should == "USER anonymous\r\n"; s.print "331 name ok\r\n"
|
|
602
|
+
s.gets.should match /\APASS .*\r\n\z/; s.print "230 logged in\r\n"
|
|
603
|
+
s.gets.should == "TYPE I\r\n"; s.print "200 type set to I\r\n"
|
|
604
|
+
s.gets.should == "CWD /foo\r\n"; s.print "250 CWD successful\r\n"
|
|
605
|
+
s.gets.should == "TYPE A\r\n"; s.print "200 type set to A\r\n"
|
|
606
|
+
s.gets.should == "SIZE bar\r\n"; s.print "213 #{content.bytesize}\r\n"
|
|
607
|
+
s.gets.should == "PASV\r\n"
|
|
608
|
+
TCPServer.open("127.0.0.1", 0) {|data_serv|
|
|
609
|
+
_, data_serv_port, _, data_serv_host = data_serv.addr
|
|
610
|
+
hi = data_serv_port >> 8
|
|
611
|
+
lo = data_serv_port & 0xff
|
|
612
|
+
s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n"
|
|
613
|
+
s.gets.should == "RETR bar\r\n"; s.print "150 file okay\r\n"
|
|
614
|
+
data_sock = data_serv.accept
|
|
615
|
+
begin
|
|
616
|
+
data_sock << content
|
|
617
|
+
ensure
|
|
618
|
+
data_sock.close
|
|
619
|
+
end
|
|
620
|
+
s.print "226 transfer complete\r\n"
|
|
621
|
+
s.gets.should be nil
|
|
622
|
+
}
|
|
623
|
+
ensure
|
|
624
|
+
s.close if s
|
|
625
|
+
end
|
|
626
|
+
}
|
|
627
|
+
begin
|
|
628
|
+
length = []
|
|
629
|
+
progress = []
|
|
630
|
+
content = URI("ftp://#{host}:#{port}/%2Ffoo/b%61r;type=a").read(
|
|
631
|
+
:content_length_proc => lambda {|n| length << n },
|
|
632
|
+
:progress_proc => lambda {|n| progress << n })
|
|
633
|
+
content.should == "content"
|
|
634
|
+
length.should == [7]
|
|
635
|
+
progress.inject(&:+).should == 7
|
|
636
|
+
ensure
|
|
637
|
+
Thread.kill(th)
|
|
638
|
+
th.join
|
|
639
|
+
end
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
it 'should allow ftp via an http proxy' do
|
|
644
|
+
TCPServer.open("127.0.0.1", 0) do |proxy_serv|
|
|
645
|
+
proxy_port = proxy_serv.addr[1]
|
|
646
|
+
th = Thread.new {
|
|
647
|
+
proxy_sock = proxy_serv.accept
|
|
648
|
+
begin
|
|
649
|
+
req = proxy_sock.gets("\r\n\r\n")
|
|
650
|
+
req.should match %r{\AGET ftp://192.0.2.1/foo/bar }
|
|
651
|
+
proxy_sock.print "HTTP/1.0 200 OK\r\n"
|
|
652
|
+
proxy_sock.print "Content-Length: 4\r\n\r\n"
|
|
653
|
+
proxy_sock.print "ab\r\n"
|
|
654
|
+
ensure
|
|
655
|
+
proxy_sock.close
|
|
656
|
+
end
|
|
657
|
+
}
|
|
658
|
+
begin
|
|
659
|
+
with_env('ftp_proxy'=>"http://127.0.0.1:#{proxy_port}") {
|
|
660
|
+
content = URI("ftp://192.0.2.1/foo/bar").read
|
|
661
|
+
content.should == "ab\r\n"
|
|
662
|
+
}
|
|
663
|
+
ensure
|
|
664
|
+
Thread.kill(th)
|
|
665
|
+
th.join
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
it 'should allow ftp via an http proxy with basic authorization' do
|
|
671
|
+
TCPServer.open("127.0.0.1", 0) do |proxy_serv|
|
|
672
|
+
proxy_port = proxy_serv.addr[1]
|
|
673
|
+
th = Thread.new {
|
|
674
|
+
proxy_sock = proxy_serv.accept
|
|
675
|
+
begin
|
|
676
|
+
req = proxy_sock.gets("\r\n\r\n")
|
|
677
|
+
req.should match %r{\AGET ftp://192.0.2.1/foo/bar }
|
|
678
|
+
req.should match %r{Proxy-Authorization: Basic #{['proxy-user:proxy-password'].pack('m').chomp}\r\n}
|
|
679
|
+
proxy_sock.print "HTTP/1.0 200 OK\r\n"
|
|
680
|
+
proxy_sock.print "Content-Length: 4\r\n\r\n"
|
|
681
|
+
proxy_sock.print "ab\r\n"
|
|
682
|
+
ensure
|
|
683
|
+
proxy_sock.close
|
|
684
|
+
end
|
|
685
|
+
}
|
|
686
|
+
begin
|
|
687
|
+
content = URI("ftp://192.0.2.1/foo/bar").read(
|
|
688
|
+
:proxy_http_basic_authentication => ["http://127.0.0.1:#{proxy_port}", "proxy-user", "proxy-password"])
|
|
689
|
+
content.should == "ab\r\n"
|
|
690
|
+
ensure
|
|
691
|
+
Thread.kill(th)
|
|
692
|
+
th.join
|
|
693
|
+
end
|
|
694
|
+
end
|
|
695
|
+
end
|
|
696
|
+
end
|
|
697
|
+
end
|