##// END OF EJS Templates
tests: fix error case in test-url.py's doctest...
Augie Fackler -
r37891:0dcd0363 default
parent child Browse files
Show More
@@ -1,421 +1,423 b''
1 # coding=utf-8
1 # coding=utf-8
2 from __future__ import absolute_import, print_function
2 from __future__ import absolute_import, print_function
3
3
4 import doctest
4 import doctest
5 import os
5 import os
6
6
7 def check(a, b):
7 def check(a, b):
8 if a != b:
8 if a != b:
9 print((a, b))
9 print((a, b))
10
10
11 def cert(cn):
11 def cert(cn):
12 return {'subject': ((('commonName', cn),),)}
12 return {'subject': ((('commonName', cn),),)}
13
13
14 from mercurial import (
14 from mercurial import (
15 sslutil,
15 sslutil,
16 )
16 )
17
17
18 _verifycert = sslutil._verifycert
18 _verifycert = sslutil._verifycert
19 # Test non-wildcard certificates
19 # Test non-wildcard certificates
20 check(_verifycert(cert('example.com'), 'example.com'),
20 check(_verifycert(cert('example.com'), 'example.com'),
21 None)
21 None)
22 check(_verifycert(cert('example.com'), 'www.example.com'),
22 check(_verifycert(cert('example.com'), 'www.example.com'),
23 'certificate is for example.com')
23 'certificate is for example.com')
24 check(_verifycert(cert('www.example.com'), 'example.com'),
24 check(_verifycert(cert('www.example.com'), 'example.com'),
25 'certificate is for www.example.com')
25 'certificate is for www.example.com')
26
26
27 # Test wildcard certificates
27 # Test wildcard certificates
28 check(_verifycert(cert('*.example.com'), 'www.example.com'),
28 check(_verifycert(cert('*.example.com'), 'www.example.com'),
29 None)
29 None)
30 check(_verifycert(cert('*.example.com'), 'example.com'),
30 check(_verifycert(cert('*.example.com'), 'example.com'),
31 'certificate is for *.example.com')
31 'certificate is for *.example.com')
32 check(_verifycert(cert('*.example.com'), 'w.w.example.com'),
32 check(_verifycert(cert('*.example.com'), 'w.w.example.com'),
33 'certificate is for *.example.com')
33 'certificate is for *.example.com')
34
34
35 # Test subjectAltName
35 # Test subjectAltName
36 san_cert = {'subject': ((('commonName', 'example.com'),),),
36 san_cert = {'subject': ((('commonName', 'example.com'),),),
37 'subjectAltName': (('DNS', '*.example.net'),
37 'subjectAltName': (('DNS', '*.example.net'),
38 ('DNS', 'example.net'))}
38 ('DNS', 'example.net'))}
39 check(_verifycert(san_cert, 'example.net'),
39 check(_verifycert(san_cert, 'example.net'),
40 None)
40 None)
41 check(_verifycert(san_cert, 'foo.example.net'),
41 check(_verifycert(san_cert, 'foo.example.net'),
42 None)
42 None)
43 # no fallback to subject commonName when subjectAltName has DNS
43 # no fallback to subject commonName when subjectAltName has DNS
44 check(_verifycert(san_cert, 'example.com'),
44 check(_verifycert(san_cert, 'example.com'),
45 'certificate is for *.example.net, example.net')
45 'certificate is for *.example.net, example.net')
46 # fallback to subject commonName when no DNS in subjectAltName
46 # fallback to subject commonName when no DNS in subjectAltName
47 san_cert = {'subject': ((('commonName', 'example.com'),),),
47 san_cert = {'subject': ((('commonName', 'example.com'),),),
48 'subjectAltName': (('IP Address', '8.8.8.8'),)}
48 'subjectAltName': (('IP Address', '8.8.8.8'),)}
49 check(_verifycert(san_cert, 'example.com'), None)
49 check(_verifycert(san_cert, 'example.com'), None)
50
50
51 # Avoid some pitfalls
51 # Avoid some pitfalls
52 check(_verifycert(cert('*.foo'), 'foo'),
52 check(_verifycert(cert('*.foo'), 'foo'),
53 'certificate is for *.foo')
53 'certificate is for *.foo')
54 check(_verifycert(cert('*o'), 'foo'), None)
54 check(_verifycert(cert('*o'), 'foo'), None)
55
55
56 check(_verifycert({'subject': ()},
56 check(_verifycert({'subject': ()},
57 'example.com'),
57 'example.com'),
58 'no commonName or subjectAltName found in certificate')
58 'no commonName or subjectAltName found in certificate')
59 check(_verifycert(None, 'example.com'),
59 check(_verifycert(None, 'example.com'),
60 'no certificate received')
60 'no certificate received')
61
61
62 # Unicode (IDN) certname isn't supported
62 # Unicode (IDN) certname isn't supported
63 check(_verifycert(cert(u'\u4f8b.jp'), 'example.jp'),
63 check(_verifycert(cert(u'\u4f8b.jp'), 'example.jp'),
64 'IDN in certificate not supported')
64 'IDN in certificate not supported')
65
65
66 # The following tests are from CPython's test_ssl.py.
66 # The following tests are from CPython's test_ssl.py.
67 check(_verifycert(cert('example.com'), 'example.com'), None)
67 check(_verifycert(cert('example.com'), 'example.com'), None)
68 check(_verifycert(cert('example.com'), 'ExAmple.cOm'), None)
68 check(_verifycert(cert('example.com'), 'ExAmple.cOm'), None)
69 check(_verifycert(cert('example.com'), 'www.example.com'),
69 check(_verifycert(cert('example.com'), 'www.example.com'),
70 'certificate is for example.com')
70 'certificate is for example.com')
71 check(_verifycert(cert('example.com'), '.example.com'),
71 check(_verifycert(cert('example.com'), '.example.com'),
72 'certificate is for example.com')
72 'certificate is for example.com')
73 check(_verifycert(cert('example.com'), 'example.org'),
73 check(_verifycert(cert('example.com'), 'example.org'),
74 'certificate is for example.com')
74 'certificate is for example.com')
75 check(_verifycert(cert('example.com'), 'exampleXcom'),
75 check(_verifycert(cert('example.com'), 'exampleXcom'),
76 'certificate is for example.com')
76 'certificate is for example.com')
77 check(_verifycert(cert('*.a.com'), 'foo.a.com'), None)
77 check(_verifycert(cert('*.a.com'), 'foo.a.com'), None)
78 check(_verifycert(cert('*.a.com'), 'bar.foo.a.com'),
78 check(_verifycert(cert('*.a.com'), 'bar.foo.a.com'),
79 'certificate is for *.a.com')
79 'certificate is for *.a.com')
80 check(_verifycert(cert('*.a.com'), 'a.com'),
80 check(_verifycert(cert('*.a.com'), 'a.com'),
81 'certificate is for *.a.com')
81 'certificate is for *.a.com')
82 check(_verifycert(cert('*.a.com'), 'Xa.com'),
82 check(_verifycert(cert('*.a.com'), 'Xa.com'),
83 'certificate is for *.a.com')
83 'certificate is for *.a.com')
84 check(_verifycert(cert('*.a.com'), '.a.com'),
84 check(_verifycert(cert('*.a.com'), '.a.com'),
85 'certificate is for *.a.com')
85 'certificate is for *.a.com')
86
86
87 # only match one left-most wildcard
87 # only match one left-most wildcard
88 check(_verifycert(cert('f*.com'), 'foo.com'), None)
88 check(_verifycert(cert('f*.com'), 'foo.com'), None)
89 check(_verifycert(cert('f*.com'), 'f.com'), None)
89 check(_verifycert(cert('f*.com'), 'f.com'), None)
90 check(_verifycert(cert('f*.com'), 'bar.com'),
90 check(_verifycert(cert('f*.com'), 'bar.com'),
91 'certificate is for f*.com')
91 'certificate is for f*.com')
92 check(_verifycert(cert('f*.com'), 'foo.a.com'),
92 check(_verifycert(cert('f*.com'), 'foo.a.com'),
93 'certificate is for f*.com')
93 'certificate is for f*.com')
94 check(_verifycert(cert('f*.com'), 'bar.foo.com'),
94 check(_verifycert(cert('f*.com'), 'bar.foo.com'),
95 'certificate is for f*.com')
95 'certificate is for f*.com')
96
96
97 # NULL bytes are bad, CVE-2013-4073
97 # NULL bytes are bad, CVE-2013-4073
98 check(_verifycert(cert('null.python.org\x00example.org'),
98 check(_verifycert(cert('null.python.org\x00example.org'),
99 'null.python.org\x00example.org'), None)
99 'null.python.org\x00example.org'), None)
100 check(_verifycert(cert('null.python.org\x00example.org'),
100 check(_verifycert(cert('null.python.org\x00example.org'),
101 'example.org'),
101 'example.org'),
102 'certificate is for null.python.org\x00example.org')
102 'certificate is for null.python.org\x00example.org')
103 check(_verifycert(cert('null.python.org\x00example.org'),
103 check(_verifycert(cert('null.python.org\x00example.org'),
104 'null.python.org'),
104 'null.python.org'),
105 'certificate is for null.python.org\x00example.org')
105 'certificate is for null.python.org\x00example.org')
106
106
107 # error cases with wildcards
107 # error cases with wildcards
108 check(_verifycert(cert('*.*.a.com'), 'bar.foo.a.com'),
108 check(_verifycert(cert('*.*.a.com'), 'bar.foo.a.com'),
109 'certificate is for *.*.a.com')
109 'certificate is for *.*.a.com')
110 check(_verifycert(cert('*.*.a.com'), 'a.com'),
110 check(_verifycert(cert('*.*.a.com'), 'a.com'),
111 'certificate is for *.*.a.com')
111 'certificate is for *.*.a.com')
112 check(_verifycert(cert('*.*.a.com'), 'Xa.com'),
112 check(_verifycert(cert('*.*.a.com'), 'Xa.com'),
113 'certificate is for *.*.a.com')
113 'certificate is for *.*.a.com')
114 check(_verifycert(cert('*.*.a.com'), '.a.com'),
114 check(_verifycert(cert('*.*.a.com'), '.a.com'),
115 'certificate is for *.*.a.com')
115 'certificate is for *.*.a.com')
116
116
117 check(_verifycert(cert('a.*.com'), 'a.foo.com'),
117 check(_verifycert(cert('a.*.com'), 'a.foo.com'),
118 'certificate is for a.*.com')
118 'certificate is for a.*.com')
119 check(_verifycert(cert('a.*.com'), 'a..com'),
119 check(_verifycert(cert('a.*.com'), 'a..com'),
120 'certificate is for a.*.com')
120 'certificate is for a.*.com')
121 check(_verifycert(cert('a.*.com'), 'a.com'),
121 check(_verifycert(cert('a.*.com'), 'a.com'),
122 'certificate is for a.*.com')
122 'certificate is for a.*.com')
123
123
124 # wildcard doesn't match IDNA prefix 'xn--'
124 # wildcard doesn't match IDNA prefix 'xn--'
125 idna = u'pΓΌthon.python.org'.encode('idna').decode('ascii')
125 idna = u'pΓΌthon.python.org'.encode('idna').decode('ascii')
126 check(_verifycert(cert(idna), idna), None)
126 check(_verifycert(cert(idna), idna), None)
127 check(_verifycert(cert('x*.python.org'), idna),
127 check(_verifycert(cert('x*.python.org'), idna),
128 'certificate is for x*.python.org')
128 'certificate is for x*.python.org')
129 check(_verifycert(cert('xn--p*.python.org'), idna),
129 check(_verifycert(cert('xn--p*.python.org'), idna),
130 'certificate is for xn--p*.python.org')
130 'certificate is for xn--p*.python.org')
131
131
132 # wildcard in first fragment and IDNA A-labels in sequent fragments
132 # wildcard in first fragment and IDNA A-labels in sequent fragments
133 # are supported.
133 # are supported.
134 idna = u'www*.pythΓΆn.org'.encode('idna').decode('ascii')
134 idna = u'www*.pythΓΆn.org'.encode('idna').decode('ascii')
135 check(_verifycert(cert(idna),
135 check(_verifycert(cert(idna),
136 u'www.pythΓΆn.org'.encode('idna').decode('ascii')),
136 u'www.pythΓΆn.org'.encode('idna').decode('ascii')),
137 None)
137 None)
138 check(_verifycert(cert(idna),
138 check(_verifycert(cert(idna),
139 u'www1.pythΓΆn.org'.encode('idna').decode('ascii')),
139 u'www1.pythΓΆn.org'.encode('idna').decode('ascii')),
140 None)
140 None)
141 check(_verifycert(cert(idna),
141 check(_verifycert(cert(idna),
142 u'ftp.pythΓΆn.org'.encode('idna').decode('ascii')),
142 u'ftp.pythΓΆn.org'.encode('idna').decode('ascii')),
143 'certificate is for www*.xn--pythn-mua.org')
143 'certificate is for www*.xn--pythn-mua.org')
144 check(_verifycert(cert(idna),
144 check(_verifycert(cert(idna),
145 u'pythΓΆn.org'.encode('idna').decode('ascii')),
145 u'pythΓΆn.org'.encode('idna').decode('ascii')),
146 'certificate is for www*.xn--pythn-mua.org')
146 'certificate is for www*.xn--pythn-mua.org')
147
147
148 c = {
148 c = {
149 'notAfter': 'Jun 26 21:41:46 2011 GMT',
149 'notAfter': 'Jun 26 21:41:46 2011 GMT',
150 'subject': (((u'commonName', u'linuxfrz.org'),),),
150 'subject': (((u'commonName', u'linuxfrz.org'),),),
151 'subjectAltName': (
151 'subjectAltName': (
152 ('DNS', 'linuxfr.org'),
152 ('DNS', 'linuxfr.org'),
153 ('DNS', 'linuxfr.com'),
153 ('DNS', 'linuxfr.com'),
154 ('othername', '<unsupported>'),
154 ('othername', '<unsupported>'),
155 )
155 )
156 }
156 }
157 check(_verifycert(c, 'linuxfr.org'), None)
157 check(_verifycert(c, 'linuxfr.org'), None)
158 check(_verifycert(c, 'linuxfr.com'), None)
158 check(_verifycert(c, 'linuxfr.com'), None)
159 # Not a "DNS" entry
159 # Not a "DNS" entry
160 check(_verifycert(c, '<unsupported>'),
160 check(_verifycert(c, '<unsupported>'),
161 'certificate is for linuxfr.org, linuxfr.com')
161 'certificate is for linuxfr.org, linuxfr.com')
162 # When there is a subjectAltName, commonName isn't used
162 # When there is a subjectAltName, commonName isn't used
163 check(_verifycert(c, 'linuxfrz.org'),
163 check(_verifycert(c, 'linuxfrz.org'),
164 'certificate is for linuxfr.org, linuxfr.com')
164 'certificate is for linuxfr.org, linuxfr.com')
165
165
166 # A pristine real-world example
166 # A pristine real-world example
167 c = {
167 c = {
168 'notAfter': 'Dec 18 23:59:59 2011 GMT',
168 'notAfter': 'Dec 18 23:59:59 2011 GMT',
169 'subject': (
169 'subject': (
170 ((u'countryName', u'US'),),
170 ((u'countryName', u'US'),),
171 ((u'stateOrProvinceName', u'California'),),
171 ((u'stateOrProvinceName', u'California'),),
172 ((u'localityName', u'Mountain View'),),
172 ((u'localityName', u'Mountain View'),),
173 ((u'organizationName', u'Google Inc'),),
173 ((u'organizationName', u'Google Inc'),),
174 ((u'commonName', u'mail.google.com'),),
174 ((u'commonName', u'mail.google.com'),),
175 ),
175 ),
176 }
176 }
177 check(_verifycert(c, 'mail.google.com'), None)
177 check(_verifycert(c, 'mail.google.com'), None)
178 check(_verifycert(c, 'gmail.com'), 'certificate is for mail.google.com')
178 check(_verifycert(c, 'gmail.com'), 'certificate is for mail.google.com')
179
179
180 # Only commonName is considered
180 # Only commonName is considered
181 check(_verifycert(c, 'California'), 'certificate is for mail.google.com')
181 check(_verifycert(c, 'California'), 'certificate is for mail.google.com')
182
182
183 # Neither commonName nor subjectAltName
183 # Neither commonName nor subjectAltName
184 c = {
184 c = {
185 'notAfter': 'Dec 18 23:59:59 2011 GMT',
185 'notAfter': 'Dec 18 23:59:59 2011 GMT',
186 'subject': (
186 'subject': (
187 ((u'countryName', u'US'),),
187 ((u'countryName', u'US'),),
188 ((u'stateOrProvinceName', u'California'),),
188 ((u'stateOrProvinceName', u'California'),),
189 ((u'localityName', u'Mountain View'),),
189 ((u'localityName', u'Mountain View'),),
190 ((u'organizationName', u'Google Inc'),),
190 ((u'organizationName', u'Google Inc'),),
191 ),
191 ),
192 }
192 }
193 check(_verifycert(c, 'mail.google.com'),
193 check(_verifycert(c, 'mail.google.com'),
194 'no commonName or subjectAltName found in certificate')
194 'no commonName or subjectAltName found in certificate')
195
195
196 # No DNS entry in subjectAltName but a commonName
196 # No DNS entry in subjectAltName but a commonName
197 c = {
197 c = {
198 'notAfter': 'Dec 18 23:59:59 2099 GMT',
198 'notAfter': 'Dec 18 23:59:59 2099 GMT',
199 'subject': (
199 'subject': (
200 ((u'countryName', u'US'),),
200 ((u'countryName', u'US'),),
201 ((u'stateOrProvinceName', u'California'),),
201 ((u'stateOrProvinceName', u'California'),),
202 ((u'localityName', u'Mountain View'),),
202 ((u'localityName', u'Mountain View'),),
203 ((u'commonName', u'mail.google.com'),),
203 ((u'commonName', u'mail.google.com'),),
204 ),
204 ),
205 'subjectAltName': (('othername', 'blabla'),),
205 'subjectAltName': (('othername', 'blabla'),),
206 }
206 }
207 check(_verifycert(c, 'mail.google.com'), None)
207 check(_verifycert(c, 'mail.google.com'), None)
208
208
209 # No DNS entry subjectAltName and no commonName
209 # No DNS entry subjectAltName and no commonName
210 c = {
210 c = {
211 'notAfter': 'Dec 18 23:59:59 2099 GMT',
211 'notAfter': 'Dec 18 23:59:59 2099 GMT',
212 'subject': (
212 'subject': (
213 ((u'countryName', u'US'),),
213 ((u'countryName', u'US'),),
214 ((u'stateOrProvinceName', u'California'),),
214 ((u'stateOrProvinceName', u'California'),),
215 ((u'localityName', u'Mountain View'),),
215 ((u'localityName', u'Mountain View'),),
216 ((u'organizationName', u'Google Inc'),),
216 ((u'organizationName', u'Google Inc'),),
217 ),
217 ),
218 'subjectAltName': (('othername', 'blabla'),),
218 'subjectAltName': (('othername', 'blabla'),),
219 }
219 }
220 check(_verifycert(c, 'google.com'),
220 check(_verifycert(c, 'google.com'),
221 'no commonName or subjectAltName found in certificate')
221 'no commonName or subjectAltName found in certificate')
222
222
223 # Empty cert / no cert
223 # Empty cert / no cert
224 check(_verifycert(None, 'example.com'), 'no certificate received')
224 check(_verifycert(None, 'example.com'), 'no certificate received')
225 check(_verifycert({}, 'example.com'), 'no certificate received')
225 check(_verifycert({}, 'example.com'), 'no certificate received')
226
226
227 # avoid denials of service by refusing more than one
227 # avoid denials of service by refusing more than one
228 # wildcard per fragment.
228 # wildcard per fragment.
229 check(_verifycert({'subject': (((u'commonName', u'a*b.com'),),)},
229 check(_verifycert({'subject': (((u'commonName', u'a*b.com'),),)},
230 'axxb.com'), None)
230 'axxb.com'), None)
231 check(_verifycert({'subject': (((u'commonName', u'a*b.co*'),),)},
231 check(_verifycert({'subject': (((u'commonName', u'a*b.co*'),),)},
232 'axxb.com'), 'certificate is for a*b.co*')
232 'axxb.com'), 'certificate is for a*b.co*')
233 check(_verifycert({'subject': (((u'commonName', u'a*b*.com'),),)},
233 check(_verifycert({'subject': (((u'commonName', u'a*b*.com'),),)},
234 'axxbxxc.com'),
234 'axxbxxc.com'),
235 'too many wildcards in certificate DNS name: a*b*.com')
235 'too many wildcards in certificate DNS name: a*b*.com')
236
236
237 def test_url():
237 def test_url():
238 """
238 """
239 >>> from mercurial.util import url
239 >>> from mercurial import error, pycompat
240 >>> from mercurial.util import forcebytestr, url
240
241
241 This tests for edge cases in url.URL's parsing algorithm. Most of
242 This tests for edge cases in url.URL's parsing algorithm. Most of
242 these aren't useful for documentation purposes, so they aren't
243 these aren't useful for documentation purposes, so they aren't
243 part of the class's doc tests.
244 part of the class's doc tests.
244
245
245 Query strings and fragments:
246 Query strings and fragments:
246
247
247 >>> url('http://host/a?b#c')
248 >>> url('http://host/a?b#c')
248 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
249 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
249 >>> url('http://host/a?')
250 >>> url('http://host/a?')
250 <url scheme: 'http', host: 'host', path: 'a'>
251 <url scheme: 'http', host: 'host', path: 'a'>
251 >>> url('http://host/a#b#c')
252 >>> url('http://host/a#b#c')
252 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b#c'>
253 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b#c'>
253 >>> url('http://host/a#b?c')
254 >>> url('http://host/a#b?c')
254 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b?c'>
255 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b?c'>
255 >>> url('http://host/?a#b')
256 >>> url('http://host/?a#b')
256 <url scheme: 'http', host: 'host', path: '', query: 'a', fragment: 'b'>
257 <url scheme: 'http', host: 'host', path: '', query: 'a', fragment: 'b'>
257 >>> url('http://host/?a#b', parsequery=False)
258 >>> url('http://host/?a#b', parsequery=False)
258 <url scheme: 'http', host: 'host', path: '?a', fragment: 'b'>
259 <url scheme: 'http', host: 'host', path: '?a', fragment: 'b'>
259 >>> url('http://host/?a#b', parsefragment=False)
260 >>> url('http://host/?a#b', parsefragment=False)
260 <url scheme: 'http', host: 'host', path: '', query: 'a#b'>
261 <url scheme: 'http', host: 'host', path: '', query: 'a#b'>
261 >>> url('http://host/?a#b', parsequery=False, parsefragment=False)
262 >>> url('http://host/?a#b', parsequery=False, parsefragment=False)
262 <url scheme: 'http', host: 'host', path: '?a#b'>
263 <url scheme: 'http', host: 'host', path: '?a#b'>
263
264
264 IPv6 addresses:
265 IPv6 addresses:
265
266
266 >>> url('ldap://[2001:db8::7]/c=GB?objectClass?one')
267 >>> url('ldap://[2001:db8::7]/c=GB?objectClass?one')
267 <url scheme: 'ldap', host: '[2001:db8::7]', path: 'c=GB',
268 <url scheme: 'ldap', host: '[2001:db8::7]', path: 'c=GB',
268 query: 'objectClass?one'>
269 query: 'objectClass?one'>
269 >>> url('ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one')
270 >>> url('ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one')
270 <url scheme: 'ldap', user: 'joe', passwd: 'xxx', host: '[2001:db8::7]',
271 <url scheme: 'ldap', user: 'joe', passwd: 'xxx', host: '[2001:db8::7]',
271 port: '80', path: 'c=GB', query: 'objectClass?one'>
272 port: '80', path: 'c=GB', query: 'objectClass?one'>
272
273
273 Missing scheme, host, etc.:
274 Missing scheme, host, etc.:
274
275
275 >>> url('://192.0.2.16:80/')
276 >>> url('://192.0.2.16:80/')
276 <url path: '://192.0.2.16:80/'>
277 <url path: '://192.0.2.16:80/'>
277 >>> url('https://mercurial-scm.org')
278 >>> url('https://mercurial-scm.org')
278 <url scheme: 'https', host: 'mercurial-scm.org'>
279 <url scheme: 'https', host: 'mercurial-scm.org'>
279 >>> url('/foo')
280 >>> url('/foo')
280 <url path: '/foo'>
281 <url path: '/foo'>
281 >>> url('bundle:/foo')
282 >>> url('bundle:/foo')
282 <url scheme: 'bundle', path: '/foo'>
283 <url scheme: 'bundle', path: '/foo'>
283 >>> url('a?b#c')
284 >>> url('a?b#c')
284 <url path: 'a?b', fragment: 'c'>
285 <url path: 'a?b', fragment: 'c'>
285 >>> url('http://x.com?arg=/foo')
286 >>> url('http://x.com?arg=/foo')
286 <url scheme: 'http', host: 'x.com', query: 'arg=/foo'>
287 <url scheme: 'http', host: 'x.com', query: 'arg=/foo'>
287 >>> url('http://joe:xxx@/foo')
288 >>> url('http://joe:xxx@/foo')
288 <url scheme: 'http', user: 'joe', passwd: 'xxx', path: 'foo'>
289 <url scheme: 'http', user: 'joe', passwd: 'xxx', path: 'foo'>
289
290
290 Just a scheme and a path:
291 Just a scheme and a path:
291
292
292 >>> url('mailto:John.Doe@example.com')
293 >>> url('mailto:John.Doe@example.com')
293 <url scheme: 'mailto', path: 'John.Doe@example.com'>
294 <url scheme: 'mailto', path: 'John.Doe@example.com'>
294 >>> url('a:b:c:d')
295 >>> url('a:b:c:d')
295 <url path: 'a:b:c:d'>
296 <url path: 'a:b:c:d'>
296 >>> url('aa:bb:cc:dd')
297 >>> url('aa:bb:cc:dd')
297 <url scheme: 'aa', path: 'bb:cc:dd'>
298 <url scheme: 'aa', path: 'bb:cc:dd'>
298
299
299 SSH examples:
300 SSH examples:
300
301
301 >>> url('ssh://joe@host//home/joe')
302 >>> url('ssh://joe@host//home/joe')
302 <url scheme: 'ssh', user: 'joe', host: 'host', path: '/home/joe'>
303 <url scheme: 'ssh', user: 'joe', host: 'host', path: '/home/joe'>
303 >>> url('ssh://joe:xxx@host/src')
304 >>> url('ssh://joe:xxx@host/src')
304 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', path: 'src'>
305 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', path: 'src'>
305 >>> url('ssh://joe:xxx@host')
306 >>> url('ssh://joe:xxx@host')
306 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host'>
307 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host'>
307 >>> url('ssh://joe@host')
308 >>> url('ssh://joe@host')
308 <url scheme: 'ssh', user: 'joe', host: 'host'>
309 <url scheme: 'ssh', user: 'joe', host: 'host'>
309 >>> url('ssh://host')
310 >>> url('ssh://host')
310 <url scheme: 'ssh', host: 'host'>
311 <url scheme: 'ssh', host: 'host'>
311 >>> url('ssh://')
312 >>> url('ssh://')
312 <url scheme: 'ssh'>
313 <url scheme: 'ssh'>
313 >>> url('ssh:')
314 >>> url('ssh:')
314 <url scheme: 'ssh'>
315 <url scheme: 'ssh'>
315
316
316 Non-numeric port:
317 Non-numeric port:
317
318
318 >>> url('http://example.com:dd')
319 >>> url('http://example.com:dd')
319 <url scheme: 'http', host: 'example.com', port: 'dd'>
320 <url scheme: 'http', host: 'example.com', port: 'dd'>
320 >>> url('ssh://joe:xxx@host:ssh/foo')
321 >>> url('ssh://joe:xxx@host:ssh/foo')
321 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', port: 'ssh',
322 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', port: 'ssh',
322 path: 'foo'>
323 path: 'foo'>
323
324
324 Bad authentication credentials:
325 Bad authentication credentials:
325
326
326 >>> url('http://joe@joeville:123@4:@host/a?b#c')
327 >>> url('http://joe@joeville:123@4:@host/a?b#c')
327 <url scheme: 'http', user: 'joe@joeville', passwd: '123@4:',
328 <url scheme: 'http', user: 'joe@joeville', passwd: '123@4:',
328 host: 'host', path: 'a', query: 'b', fragment: 'c'>
329 host: 'host', path: 'a', query: 'b', fragment: 'c'>
329 >>> url('http://!*#?/@!*#?/:@host/a?b#c')
330 >>> url('http://!*#?/@!*#?/:@host/a?b#c')
330 <url scheme: 'http', host: '!*', fragment: '?/@!*#?/:@host/a?b#c'>
331 <url scheme: 'http', host: '!*', fragment: '?/@!*#?/:@host/a?b#c'>
331 >>> url('http://!*#?@!*#?:@host/a?b#c')
332 >>> url('http://!*#?@!*#?:@host/a?b#c')
332 <url scheme: 'http', host: '!*', fragment: '?@!*#?:@host/a?b#c'>
333 <url scheme: 'http', host: '!*', fragment: '?@!*#?:@host/a?b#c'>
333 >>> url('http://!*@:!*@@host/a?b#c')
334 >>> url('http://!*@:!*@@host/a?b#c')
334 <url scheme: 'http', user: '!*@', passwd: '!*@', host: 'host',
335 <url scheme: 'http', user: '!*@', passwd: '!*@', host: 'host',
335 path: 'a', query: 'b', fragment: 'c'>
336 path: 'a', query: 'b', fragment: 'c'>
336
337
337 File paths:
338 File paths:
338
339
339 >>> url('a/b/c/d.g.f')
340 >>> url('a/b/c/d.g.f')
340 <url path: 'a/b/c/d.g.f'>
341 <url path: 'a/b/c/d.g.f'>
341 >>> url('/x///z/y/')
342 >>> url('/x///z/y/')
342 <url path: '/x///z/y/'>
343 <url path: '/x///z/y/'>
343 >>> url('/foo:bar')
344 >>> url('/foo:bar')
344 <url path: '/foo:bar'>
345 <url path: '/foo:bar'>
345 >>> url('\\\\foo:bar')
346 >>> url('\\\\foo:bar')
346 <url path: '\\\\foo:bar'>
347 <url path: '\\\\foo:bar'>
347 >>> url('./foo:bar')
348 >>> url('./foo:bar')
348 <url path: './foo:bar'>
349 <url path: './foo:bar'>
349
350
350 Non-localhost file URL:
351 Non-localhost file URL:
351
352
352 >>> u = url('file://mercurial-scm.org/foo')
353 >>> try:
353 Traceback (most recent call last):
354 ... u = url(b'file://mercurial-scm.org/foo')
354 File "<stdin>", line 1, in ?
355 ... except error.Abort as e:
355 Abort: file:// URLs can only refer to localhost
356 ... forcebytestr(e)
357 'file:// URLs can only refer to localhost'
356
358
357 Empty URL:
359 Empty URL:
358
360
359 >>> u = url('')
361 >>> u = url('')
360 >>> u
362 >>> u
361 <url path: ''>
363 <url path: ''>
362 >>> str(u)
364 >>> str(u)
363 ''
365 ''
364
366
365 Empty path with query string:
367 Empty path with query string:
366
368
367 >>> str(url('http://foo/?bar'))
369 >>> str(url('http://foo/?bar'))
368 'http://foo/?bar'
370 'http://foo/?bar'
369
371
370 Invalid path:
372 Invalid path:
371
373
372 >>> u = url('http://foo/bar')
374 >>> u = url('http://foo/bar')
373 >>> u.path = 'bar'
375 >>> u.path = 'bar'
374 >>> str(u)
376 >>> str(u)
375 'http://foo/bar'
377 'http://foo/bar'
376
378
377 >>> u = url('file:/foo/bar/baz')
379 >>> u = url('file:/foo/bar/baz')
378 >>> u
380 >>> u
379 <url scheme: 'file', path: '/foo/bar/baz'>
381 <url scheme: 'file', path: '/foo/bar/baz'>
380 >>> str(u)
382 >>> str(u)
381 'file:///foo/bar/baz'
383 'file:///foo/bar/baz'
382 >>> u.localpath()
384 >>> u.localpath()
383 '/foo/bar/baz'
385 '/foo/bar/baz'
384
386
385 >>> u = url('file:///foo/bar/baz')
387 >>> u = url('file:///foo/bar/baz')
386 >>> u
388 >>> u
387 <url scheme: 'file', path: '/foo/bar/baz'>
389 <url scheme: 'file', path: '/foo/bar/baz'>
388 >>> str(u)
390 >>> str(u)
389 'file:///foo/bar/baz'
391 'file:///foo/bar/baz'
390 >>> u.localpath()
392 >>> u.localpath()
391 '/foo/bar/baz'
393 '/foo/bar/baz'
392
394
393 >>> u = url('file:///f:oo/bar/baz')
395 >>> u = url('file:///f:oo/bar/baz')
394 >>> u
396 >>> u
395 <url scheme: 'file', path: 'f:oo/bar/baz'>
397 <url scheme: 'file', path: 'f:oo/bar/baz'>
396 >>> str(u)
398 >>> str(u)
397 'file:///f:oo/bar/baz'
399 'file:///f:oo/bar/baz'
398 >>> u.localpath()
400 >>> u.localpath()
399 'f:oo/bar/baz'
401 'f:oo/bar/baz'
400
402
401 >>> u = url('file://localhost/f:oo/bar/baz')
403 >>> u = url('file://localhost/f:oo/bar/baz')
402 >>> u
404 >>> u
403 <url scheme: 'file', host: 'localhost', path: 'f:oo/bar/baz'>
405 <url scheme: 'file', host: 'localhost', path: 'f:oo/bar/baz'>
404 >>> str(u)
406 >>> str(u)
405 'file://localhost/f:oo/bar/baz'
407 'file://localhost/f:oo/bar/baz'
406 >>> u.localpath()
408 >>> u.localpath()
407 'f:oo/bar/baz'
409 'f:oo/bar/baz'
408
410
409 >>> u = url('file:foo/bar/baz')
411 >>> u = url('file:foo/bar/baz')
410 >>> u
412 >>> u
411 <url scheme: 'file', path: 'foo/bar/baz'>
413 <url scheme: 'file', path: 'foo/bar/baz'>
412 >>> str(u)
414 >>> str(u)
413 'file:foo/bar/baz'
415 'file:foo/bar/baz'
414 >>> u.localpath()
416 >>> u.localpath()
415 'foo/bar/baz'
417 'foo/bar/baz'
416 """
418 """
417
419
418 if 'TERM' in os.environ:
420 if 'TERM' in os.environ:
419 del os.environ['TERM']
421 del os.environ['TERM']
420
422
421 doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)
423 doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)
General Comments 0
You need to be logged in to leave comments. Login now