comparison tests/test-url.py @ 37875:078c3eec2d5c

tests: port test-url.py to Python 3 # skip-blame it's all b prefixes and pycompat.bytestr() wrappers Differential Revision: https://phab.mercurial-scm.org/D3470
author Augie Fackler <augie@google.com>
date Fri, 27 Apr 2018 00:08:48 -0400
parents 0dcd03637d36
children d088810c496e
comparison
equal deleted inserted replaced
37874:0dcd03637d36 37875:078c3eec2d5c
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 b'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 b'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 b'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 b'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'))}
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 b'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 b'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 b'no commonName or subjectAltName found in certificate')
59 check(_verifycert(None, 'example.com'), 59 check(_verifycert(None, 'example.com'),
60 'no certificate received') 60 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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 b'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),
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 b'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 b'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': (
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 b'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 b'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': (
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'), b'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'), b'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': (
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 b'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': (
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 b'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'), b'no certificate received')
225 check(_verifycert({}, 'example.com'), 'no certificate received') 225 check(_verifycert({}, 'example.com'), b'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'), b'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 b'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 import error, pycompat 239 >>> from mercurial import error, pycompat
240 >>> from mercurial.util import forcebytestr, url 240 >>> from mercurial.util import forcebytestr, url
243 these aren't useful for documentation purposes, so they aren't 243 these aren't useful for documentation purposes, so they aren't
244 part of the class's doc tests. 244 part of the class's doc tests.
245 245
246 Query strings and fragments: 246 Query strings and fragments:
247 247
248 >>> url('http://host/a?b#c') 248 >>> url(b'http://host/a?b#c')
249 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'> 249 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
250 >>> url('http://host/a?') 250 >>> url(b'http://host/a?')
251 <url scheme: 'http', host: 'host', path: 'a'> 251 <url scheme: 'http', host: 'host', path: 'a'>
252 >>> url('http://host/a#b#c') 252 >>> url(b'http://host/a#b#c')
253 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b#c'> 253 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b#c'>
254 >>> url('http://host/a#b?c') 254 >>> url(b'http://host/a#b?c')
255 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b?c'> 255 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b?c'>
256 >>> url('http://host/?a#b') 256 >>> url(b'http://host/?a#b')
257 <url scheme: 'http', host: 'host', path: '', query: 'a', fragment: 'b'> 257 <url scheme: 'http', host: 'host', path: '', query: 'a', fragment: 'b'>
258 >>> url('http://host/?a#b', parsequery=False) 258 >>> url(b'http://host/?a#b', parsequery=False)
259 <url scheme: 'http', host: 'host', path: '?a', fragment: 'b'> 259 <url scheme: 'http', host: 'host', path: '?a', fragment: 'b'>
260 >>> url('http://host/?a#b', parsefragment=False) 260 >>> url(b'http://host/?a#b', parsefragment=False)
261 <url scheme: 'http', host: 'host', path: '', query: 'a#b'> 261 <url scheme: 'http', host: 'host', path: '', query: 'a#b'>
262 >>> url('http://host/?a#b', parsequery=False, parsefragment=False) 262 >>> url(b'http://host/?a#b', parsequery=False, parsefragment=False)
263 <url scheme: 'http', host: 'host', path: '?a#b'> 263 <url scheme: 'http', host: 'host', path: '?a#b'>
264 264
265 IPv6 addresses: 265 IPv6 addresses:
266 266
267 >>> url('ldap://[2001:db8::7]/c=GB?objectClass?one') 267 >>> url(b'ldap://[2001:db8::7]/c=GB?objectClass?one')
268 <url scheme: 'ldap', host: '[2001:db8::7]', path: 'c=GB', 268 <url scheme: 'ldap', host: '[2001:db8::7]', path: 'c=GB',
269 query: 'objectClass?one'> 269 query: 'objectClass?one'>
270 >>> url('ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one') 270 >>> url(b'ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one')
271 <url scheme: 'ldap', user: 'joe', passwd: 'xxx', host: '[2001:db8::7]', 271 <url scheme: 'ldap', user: 'joe', passwd: 'xxx', host: '[2001:db8::7]',
272 port: '80', path: 'c=GB', query: 'objectClass?one'> 272 port: '80', path: 'c=GB', query: 'objectClass?one'>
273 273
274 Missing scheme, host, etc.: 274 Missing scheme, host, etc.:
275 275
276 >>> url('://192.0.2.16:80/') 276 >>> url(b'://192.0.2.16:80/')
277 <url path: '://192.0.2.16:80/'> 277 <url path: '://192.0.2.16:80/'>
278 >>> url('https://mercurial-scm.org') 278 >>> url(b'https://mercurial-scm.org')
279 <url scheme: 'https', host: 'mercurial-scm.org'> 279 <url scheme: 'https', host: 'mercurial-scm.org'>
280 >>> url('/foo') 280 >>> url(b'/foo')
281 <url path: '/foo'> 281 <url path: '/foo'>
282 >>> url('bundle:/foo') 282 >>> url(b'bundle:/foo')
283 <url scheme: 'bundle', path: '/foo'> 283 <url scheme: 'bundle', path: '/foo'>
284 >>> url('a?b#c') 284 >>> url(b'a?b#c')
285 <url path: 'a?b', fragment: 'c'> 285 <url path: 'a?b', fragment: 'c'>
286 >>> url('http://x.com?arg=/foo') 286 >>> url(b'http://x.com?arg=/foo')
287 <url scheme: 'http', host: 'x.com', query: 'arg=/foo'> 287 <url scheme: 'http', host: 'x.com', query: 'arg=/foo'>
288 >>> url('http://joe:xxx@/foo') 288 >>> url(b'http://joe:xxx@/foo')
289 <url scheme: 'http', user: 'joe', passwd: 'xxx', path: 'foo'> 289 <url scheme: 'http', user: 'joe', passwd: 'xxx', path: 'foo'>
290 290
291 Just a scheme and a path: 291 Just a scheme and a path:
292 292
293 >>> url('mailto:John.Doe@example.com') 293 >>> url(b'mailto:John.Doe@example.com')
294 <url scheme: 'mailto', path: 'John.Doe@example.com'> 294 <url scheme: 'mailto', path: 'John.Doe@example.com'>
295 >>> url('a:b:c:d') 295 >>> url(b'a:b:c:d')
296 <url path: 'a:b:c:d'> 296 <url path: 'a:b:c:d'>
297 >>> url('aa:bb:cc:dd') 297 >>> url(b'aa:bb:cc:dd')
298 <url scheme: 'aa', path: 'bb:cc:dd'> 298 <url scheme: 'aa', path: 'bb:cc:dd'>
299 299
300 SSH examples: 300 SSH examples:
301 301
302 >>> url('ssh://joe@host//home/joe') 302 >>> url(b'ssh://joe@host//home/joe')
303 <url scheme: 'ssh', user: 'joe', host: 'host', path: '/home/joe'> 303 <url scheme: 'ssh', user: 'joe', host: 'host', path: '/home/joe'>
304 >>> url('ssh://joe:xxx@host/src') 304 >>> url(b'ssh://joe:xxx@host/src')
305 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', path: 'src'> 305 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', path: 'src'>
306 >>> url('ssh://joe:xxx@host') 306 >>> url(b'ssh://joe:xxx@host')
307 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host'> 307 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host'>
308 >>> url('ssh://joe@host') 308 >>> url(b'ssh://joe@host')
309 <url scheme: 'ssh', user: 'joe', host: 'host'> 309 <url scheme: 'ssh', user: 'joe', host: 'host'>
310 >>> url('ssh://host') 310 >>> url(b'ssh://host')
311 <url scheme: 'ssh', host: 'host'> 311 <url scheme: 'ssh', host: 'host'>
312 >>> url('ssh://') 312 >>> url(b'ssh://')
313 <url scheme: 'ssh'> 313 <url scheme: 'ssh'>
314 >>> url('ssh:') 314 >>> url(b'ssh:')
315 <url scheme: 'ssh'> 315 <url scheme: 'ssh'>
316 316
317 Non-numeric port: 317 Non-numeric port:
318 318
319 >>> url('http://example.com:dd') 319 >>> url(b'http://example.com:dd')
320 <url scheme: 'http', host: 'example.com', port: 'dd'> 320 <url scheme: 'http', host: 'example.com', port: 'dd'>
321 >>> url('ssh://joe:xxx@host:ssh/foo') 321 >>> url(b'ssh://joe:xxx@host:ssh/foo')
322 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', port: 'ssh', 322 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', port: 'ssh',
323 path: 'foo'> 323 path: 'foo'>
324 324
325 Bad authentication credentials: 325 Bad authentication credentials:
326 326
327 >>> url('http://joe@joeville:123@4:@host/a?b#c') 327 >>> url(b'http://joe@joeville:123@4:@host/a?b#c')
328 <url scheme: 'http', user: 'joe@joeville', passwd: '123@4:', 328 <url scheme: 'http', user: 'joe@joeville', passwd: '123@4:',
329 host: 'host', path: 'a', query: 'b', fragment: 'c'> 329 host: 'host', path: 'a', query: 'b', fragment: 'c'>
330 >>> url('http://!*#?/@!*#?/:@host/a?b#c') 330 >>> url(b'http://!*#?/@!*#?/:@host/a?b#c')
331 <url scheme: 'http', host: '!*', fragment: '?/@!*#?/:@host/a?b#c'> 331 <url scheme: 'http', host: '!*', fragment: '?/@!*#?/:@host/a?b#c'>
332 >>> url('http://!*#?@!*#?:@host/a?b#c') 332 >>> url(b'http://!*#?@!*#?:@host/a?b#c')
333 <url scheme: 'http', host: '!*', fragment: '?@!*#?:@host/a?b#c'> 333 <url scheme: 'http', host: '!*', fragment: '?@!*#?:@host/a?b#c'>
334 >>> url('http://!*@:!*@@host/a?b#c') 334 >>> url(b'http://!*@:!*@@host/a?b#c')
335 <url scheme: 'http', user: '!*@', passwd: '!*@', host: 'host', 335 <url scheme: 'http', user: '!*@', passwd: '!*@', host: 'host',
336 path: 'a', query: 'b', fragment: 'c'> 336 path: 'a', query: 'b', fragment: 'c'>
337 337
338 File paths: 338 File paths:
339 339
340 >>> url('a/b/c/d.g.f') 340 >>> url(b'a/b/c/d.g.f')
341 <url path: 'a/b/c/d.g.f'> 341 <url path: 'a/b/c/d.g.f'>
342 >>> url('/x///z/y/') 342 >>> url(b'/x///z/y/')
343 <url path: '/x///z/y/'> 343 <url path: '/x///z/y/'>
344 >>> url('/foo:bar') 344 >>> url(b'/foo:bar')
345 <url path: '/foo:bar'> 345 <url path: '/foo:bar'>
346 >>> url('\\\\foo:bar') 346 >>> url(b'\\\\foo:bar')
347 <url path: '\\\\foo:bar'> 347 <url path: '\\\\foo:bar'>
348 >>> url('./foo:bar') 348 >>> url(b'./foo:bar')
349 <url path: './foo:bar'> 349 <url path: './foo:bar'>
350 350
351 Non-localhost file URL: 351 Non-localhost file URL:
352 352
353 >>> try: 353 >>> try:
356 ... forcebytestr(e) 356 ... forcebytestr(e)
357 'file:// URLs can only refer to localhost' 357 'file:// URLs can only refer to localhost'
358 358
359 Empty URL: 359 Empty URL:
360 360
361 >>> u = url('') 361 >>> u = url(b'')
362 >>> u 362 >>> u
363 <url path: ''> 363 <url path: ''>
364 >>> str(u) 364 >>> str(u)
365 '' 365 ''
366 366
367 Empty path with query string: 367 Empty path with query string:
368 368
369 >>> str(url('http://foo/?bar')) 369 >>> str(url(b'http://foo/?bar'))
370 'http://foo/?bar' 370 'http://foo/?bar'
371 371
372 Invalid path: 372 Invalid path:
373 373
374 >>> u = url('http://foo/bar') 374 >>> u = url(b'http://foo/bar')
375 >>> u.path = 'bar' 375 >>> u.path = b'bar'
376 >>> str(u) 376 >>> str(u)
377 'http://foo/bar' 377 'http://foo/bar'
378 378
379 >>> u = url('file:/foo/bar/baz') 379 >>> u = url(b'file:/foo/bar/baz')
380 >>> u 380 >>> u
381 <url scheme: 'file', path: '/foo/bar/baz'> 381 <url scheme: 'file', path: '/foo/bar/baz'>
382 >>> str(u) 382 >>> str(u)
383 'file:///foo/bar/baz' 383 'file:///foo/bar/baz'
384 >>> u.localpath() 384 >>> pycompat.bytestr(u.localpath())
385 '/foo/bar/baz' 385 '/foo/bar/baz'
386 386
387 >>> u = url('file:///foo/bar/baz') 387 >>> u = url(b'file:///foo/bar/baz')
388 >>> u 388 >>> u
389 <url scheme: 'file', path: '/foo/bar/baz'> 389 <url scheme: 'file', path: '/foo/bar/baz'>
390 >>> str(u) 390 >>> str(u)
391 'file:///foo/bar/baz' 391 'file:///foo/bar/baz'
392 >>> u.localpath() 392 >>> pycompat.bytestr(u.localpath())
393 '/foo/bar/baz' 393 '/foo/bar/baz'
394 394
395 >>> u = url('file:///f:oo/bar/baz') 395 >>> u = url(b'file:///f:oo/bar/baz')
396 >>> u 396 >>> u
397 <url scheme: 'file', path: 'f:oo/bar/baz'> 397 <url scheme: 'file', path: 'f:oo/bar/baz'>
398 >>> str(u) 398 >>> str(u)
399 'file:///f:oo/bar/baz' 399 'file:///f:oo/bar/baz'
400 >>> u.localpath() 400 >>> pycompat.bytestr(u.localpath())
401 'f:oo/bar/baz' 401 'f:oo/bar/baz'
402 402
403 >>> u = url('file://localhost/f:oo/bar/baz') 403 >>> u = url(b'file://localhost/f:oo/bar/baz')
404 >>> u 404 >>> u
405 <url scheme: 'file', host: 'localhost', path: 'f:oo/bar/baz'> 405 <url scheme: 'file', host: 'localhost', path: 'f:oo/bar/baz'>
406 >>> str(u) 406 >>> str(u)
407 'file://localhost/f:oo/bar/baz' 407 'file://localhost/f:oo/bar/baz'
408 >>> u.localpath() 408 >>> pycompat.bytestr(u.localpath())
409 'f:oo/bar/baz' 409 'f:oo/bar/baz'
410 410
411 >>> u = url('file:foo/bar/baz') 411 >>> u = url(b'file:foo/bar/baz')
412 >>> u 412 >>> u
413 <url scheme: 'file', path: 'foo/bar/baz'> 413 <url scheme: 'file', path: 'foo/bar/baz'>
414 >>> str(u) 414 >>> str(u)
415 'file:foo/bar/baz' 415 'file:foo/bar/baz'
416 >>> u.localpath() 416 >>> pycompat.bytestr(u.localpath())
417 'foo/bar/baz' 417 'foo/bar/baz'
418 """ 418 """
419 419
420 if 'TERM' in os.environ: 420 if 'TERM' in os.environ:
421 del os.environ['TERM'] 421 del os.environ['TERM']