##// END OF EJS Templates
url: added authuri when login information is requested (issue3209)...
Lucas Moscovicz -
r20291:7d589d92 default
parent child Browse files
Show More
@@ -1,480 +1,484 b''
1 1 # url.py - HTTP handling for mercurial
2 2 #
3 3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 import urllib, urllib2, httplib, os, socket, cStringIO
11 11 from i18n import _
12 12 import keepalive, util, sslutil
13 13 import httpconnection as httpconnectionmod
14 14
15 15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
16 16 def __init__(self, ui):
17 17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
18 18 self.ui = ui
19 19
20 20 def find_user_password(self, realm, authuri):
21 21 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
22 22 self, realm, authuri)
23 23 user, passwd = authinfo
24 24 if user and passwd:
25 25 self._writedebug(user, passwd)
26 26 return (user, passwd)
27 27
28 28 if not user or not passwd:
29 29 res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
30 30 if res:
31 31 group, auth = res
32 32 user, passwd = auth.get('username'), auth.get('password')
33 33 self.ui.debug("using auth.%s.* for authentication\n" % group)
34 34 if not user or not passwd:
35 u = util.url(authuri)
36 u.query = None
35 37 if not self.ui.interactive():
36 raise util.Abort(_('http authorization required'))
38 raise util.Abort(_('http authorization required for %s') %
39 util.hidepassword(str(u)))
37 40
38 self.ui.write(_("http authorization required\n"))
41 self.ui.write(_("http authorization required for %s\n") %
42 util.hidepassword(str(u)))
39 43 self.ui.write(_("realm: %s\n") % realm)
40 44 if user:
41 45 self.ui.write(_("user: %s\n") % user)
42 46 else:
43 47 user = self.ui.prompt(_("user:"), default=None)
44 48
45 49 if not passwd:
46 50 passwd = self.ui.getpass()
47 51
48 52 self.add_password(realm, authuri, user, passwd)
49 53 self._writedebug(user, passwd)
50 54 return (user, passwd)
51 55
52 56 def _writedebug(self, user, passwd):
53 57 msg = _('http auth: user %s, password %s\n')
54 58 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
55 59
56 60 def find_stored_password(self, authuri):
57 61 return urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
58 62 self, None, authuri)
59 63
60 64 class proxyhandler(urllib2.ProxyHandler):
61 65 def __init__(self, ui):
62 66 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
63 67 # XXX proxyauthinfo = None
64 68
65 69 if proxyurl:
66 70 # proxy can be proper url or host[:port]
67 71 if not (proxyurl.startswith('http:') or
68 72 proxyurl.startswith('https:')):
69 73 proxyurl = 'http://' + proxyurl + '/'
70 74 proxy = util.url(proxyurl)
71 75 if not proxy.user:
72 76 proxy.user = ui.config("http_proxy", "user")
73 77 proxy.passwd = ui.config("http_proxy", "passwd")
74 78
75 79 # see if we should use a proxy for this url
76 80 no_list = ["localhost", "127.0.0.1"]
77 81 no_list.extend([p.lower() for
78 82 p in ui.configlist("http_proxy", "no")])
79 83 no_list.extend([p.strip().lower() for
80 84 p in os.getenv("no_proxy", '').split(',')
81 85 if p.strip()])
82 86 # "http_proxy.always" config is for running tests on localhost
83 87 if ui.configbool("http_proxy", "always"):
84 88 self.no_list = []
85 89 else:
86 90 self.no_list = no_list
87 91
88 92 proxyurl = str(proxy)
89 93 proxies = {'http': proxyurl, 'https': proxyurl}
90 94 ui.debug('proxying through http://%s:%s\n' %
91 95 (proxy.host, proxy.port))
92 96 else:
93 97 proxies = {}
94 98
95 99 # urllib2 takes proxy values from the environment and those
96 100 # will take precedence if found. So, if there's a config entry
97 101 # defining a proxy, drop the environment ones
98 102 if ui.config("http_proxy", "host"):
99 103 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
100 104 try:
101 105 if env in os.environ:
102 106 del os.environ[env]
103 107 except OSError:
104 108 pass
105 109
106 110 urllib2.ProxyHandler.__init__(self, proxies)
107 111 self.ui = ui
108 112
109 113 def proxy_open(self, req, proxy, type_):
110 114 host = req.get_host().split(':')[0]
111 115 for e in self.no_list:
112 116 if host == e:
113 117 return None
114 118 if e.startswith('*.') and host.endswith(e[2:]):
115 119 return None
116 120 if e.startswith('.') and host.endswith(e[1:]):
117 121 return None
118 122
119 123 # work around a bug in Python < 2.4.2
120 124 # (it leaves a "\n" at the end of Proxy-authorization headers)
121 125 baseclass = req.__class__
122 126 class _request(baseclass):
123 127 def add_header(self, key, val):
124 128 if key.lower() == 'proxy-authorization':
125 129 val = val.strip()
126 130 return baseclass.add_header(self, key, val)
127 131 req.__class__ = _request
128 132
129 133 return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
130 134
131 135 def _gen_sendfile(orgsend):
132 136 def _sendfile(self, data):
133 137 # send a file
134 138 if isinstance(data, httpconnectionmod.httpsendfile):
135 139 # if auth required, some data sent twice, so rewind here
136 140 data.seek(0)
137 141 for chunk in util.filechunkiter(data):
138 142 orgsend(self, chunk)
139 143 else:
140 144 orgsend(self, data)
141 145 return _sendfile
142 146
143 147 has_https = util.safehasattr(urllib2, 'HTTPSHandler')
144 148 if has_https:
145 149 try:
146 150 _create_connection = socket.create_connection
147 151 except AttributeError:
148 152 _GLOBAL_DEFAULT_TIMEOUT = object()
149 153
150 154 def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
151 155 source_address=None):
152 156 # lifted from Python 2.6
153 157
154 158 msg = "getaddrinfo returns an empty list"
155 159 host, port = address
156 160 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
157 161 af, socktype, proto, canonname, sa = res
158 162 sock = None
159 163 try:
160 164 sock = socket.socket(af, socktype, proto)
161 165 if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
162 166 sock.settimeout(timeout)
163 167 if source_address:
164 168 sock.bind(source_address)
165 169 sock.connect(sa)
166 170 return sock
167 171
168 172 except socket.error, msg:
169 173 if sock is not None:
170 174 sock.close()
171 175
172 176 raise socket.error(msg)
173 177
174 178 class httpconnection(keepalive.HTTPConnection):
175 179 # must be able to send big bundle as stream.
176 180 send = _gen_sendfile(keepalive.HTTPConnection.send)
177 181
178 182 def connect(self):
179 183 if has_https and self.realhostport: # use CONNECT proxy
180 184 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
181 185 self.sock.connect((self.host, self.port))
182 186 if _generic_proxytunnel(self):
183 187 # we do not support client X.509 certificates
184 188 self.sock = sslutil.ssl_wrap_socket(self.sock, None, None)
185 189 else:
186 190 keepalive.HTTPConnection.connect(self)
187 191
188 192 def getresponse(self):
189 193 proxyres = getattr(self, 'proxyres', None)
190 194 if proxyres:
191 195 if proxyres.will_close:
192 196 self.close()
193 197 self.proxyres = None
194 198 return proxyres
195 199 return keepalive.HTTPConnection.getresponse(self)
196 200
197 201 # general transaction handler to support different ways to handle
198 202 # HTTPS proxying before and after Python 2.6.3.
199 203 def _generic_start_transaction(handler, h, req):
200 204 tunnel_host = getattr(req, '_tunnel_host', None)
201 205 if tunnel_host:
202 206 if tunnel_host[:7] not in ['http://', 'https:/']:
203 207 tunnel_host = 'https://' + tunnel_host
204 208 new_tunnel = True
205 209 else:
206 210 tunnel_host = req.get_selector()
207 211 new_tunnel = False
208 212
209 213 if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
210 214 u = util.url(tunnel_host)
211 215 if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
212 216 h.realhostport = ':'.join([u.host, (u.port or '443')])
213 217 h.headers = req.headers.copy()
214 218 h.headers.update(handler.parent.addheaders)
215 219 return
216 220
217 221 h.realhostport = None
218 222 h.headers = None
219 223
220 224 def _generic_proxytunnel(self):
221 225 proxyheaders = dict(
222 226 [(x, self.headers[x]) for x in self.headers
223 227 if x.lower().startswith('proxy-')])
224 228 self._set_hostport(self.host, self.port)
225 229 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
226 230 for header in proxyheaders.iteritems():
227 231 self.send('%s: %s\r\n' % header)
228 232 self.send('\r\n')
229 233
230 234 # majority of the following code is duplicated from
231 235 # httplib.HTTPConnection as there are no adequate places to
232 236 # override functions to provide the needed functionality
233 237 res = self.response_class(self.sock,
234 238 strict=self.strict,
235 239 method=self._method)
236 240
237 241 while True:
238 242 version, status, reason = res._read_status()
239 243 if status != httplib.CONTINUE:
240 244 break
241 245 while True:
242 246 skip = res.fp.readline().strip()
243 247 if not skip:
244 248 break
245 249 res.status = status
246 250 res.reason = reason.strip()
247 251
248 252 if res.status == 200:
249 253 while True:
250 254 line = res.fp.readline()
251 255 if line == '\r\n':
252 256 break
253 257 return True
254 258
255 259 if version == 'HTTP/1.0':
256 260 res.version = 10
257 261 elif version.startswith('HTTP/1.'):
258 262 res.version = 11
259 263 elif version == 'HTTP/0.9':
260 264 res.version = 9
261 265 else:
262 266 raise httplib.UnknownProtocol(version)
263 267
264 268 if res.version == 9:
265 269 res.length = None
266 270 res.chunked = 0
267 271 res.will_close = 1
268 272 res.msg = httplib.HTTPMessage(cStringIO.StringIO())
269 273 return False
270 274
271 275 res.msg = httplib.HTTPMessage(res.fp)
272 276 res.msg.fp = None
273 277
274 278 # are we using the chunked-style of transfer encoding?
275 279 trenc = res.msg.getheader('transfer-encoding')
276 280 if trenc and trenc.lower() == "chunked":
277 281 res.chunked = 1
278 282 res.chunk_left = None
279 283 else:
280 284 res.chunked = 0
281 285
282 286 # will the connection close at the end of the response?
283 287 res.will_close = res._check_close()
284 288
285 289 # do we have a Content-Length?
286 290 # NOTE: RFC 2616, section 4.4, #3 says we ignore this if
287 291 # transfer-encoding is "chunked"
288 292 length = res.msg.getheader('content-length')
289 293 if length and not res.chunked:
290 294 try:
291 295 res.length = int(length)
292 296 except ValueError:
293 297 res.length = None
294 298 else:
295 299 if res.length < 0: # ignore nonsensical negative lengths
296 300 res.length = None
297 301 else:
298 302 res.length = None
299 303
300 304 # does the body have a fixed length? (of zero)
301 305 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
302 306 100 <= status < 200 or # 1xx codes
303 307 res._method == 'HEAD'):
304 308 res.length = 0
305 309
306 310 # if the connection remains open, and we aren't using chunked, and
307 311 # a content-length was not provided, then assume that the connection
308 312 # WILL close.
309 313 if (not res.will_close and
310 314 not res.chunked and
311 315 res.length is None):
312 316 res.will_close = 1
313 317
314 318 self.proxyres = res
315 319
316 320 return False
317 321
318 322 class httphandler(keepalive.HTTPHandler):
319 323 def http_open(self, req):
320 324 return self.do_open(httpconnection, req)
321 325
322 326 def _start_transaction(self, h, req):
323 327 _generic_start_transaction(self, h, req)
324 328 return keepalive.HTTPHandler._start_transaction(self, h, req)
325 329
326 330 if has_https:
327 331 class httpsconnection(httplib.HTTPSConnection):
328 332 response_class = keepalive.HTTPResponse
329 333 # must be able to send big bundle as stream.
330 334 send = _gen_sendfile(keepalive.safesend)
331 335 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
332 336
333 337 def connect(self):
334 338 self.sock = _create_connection((self.host, self.port))
335 339
336 340 host = self.host
337 341 if self.realhostport: # use CONNECT proxy
338 342 _generic_proxytunnel(self)
339 343 host = self.realhostport.rsplit(':', 1)[0]
340 344 self.sock = sslutil.ssl_wrap_socket(
341 345 self.sock, self.key_file, self.cert_file,
342 346 **sslutil.sslkwargs(self.ui, host))
343 347 sslutil.validator(self.ui, host)(self.sock)
344 348
345 349 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
346 350 def __init__(self, ui):
347 351 keepalive.KeepAliveHandler.__init__(self)
348 352 urllib2.HTTPSHandler.__init__(self)
349 353 self.ui = ui
350 354 self.pwmgr = passwordmgr(self.ui)
351 355
352 356 def _start_transaction(self, h, req):
353 357 _generic_start_transaction(self, h, req)
354 358 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
355 359
356 360 def https_open(self, req):
357 361 # req.get_full_url() does not contain credentials and we may
358 362 # need them to match the certificates.
359 363 url = req.get_full_url()
360 364 user, password = self.pwmgr.find_stored_password(url)
361 365 res = httpconnectionmod.readauthforuri(self.ui, url, user)
362 366 if res:
363 367 group, auth = res
364 368 self.auth = auth
365 369 self.ui.debug("using auth.%s.* for authentication\n" % group)
366 370 else:
367 371 self.auth = None
368 372 return self.do_open(self._makeconnection, req)
369 373
370 374 def _makeconnection(self, host, port=None, *args, **kwargs):
371 375 keyfile = None
372 376 certfile = None
373 377
374 378 if len(args) >= 1: # key_file
375 379 keyfile = args[0]
376 380 if len(args) >= 2: # cert_file
377 381 certfile = args[1]
378 382 args = args[2:]
379 383
380 384 # if the user has specified different key/cert files in
381 385 # hgrc, we prefer these
382 386 if self.auth and 'key' in self.auth and 'cert' in self.auth:
383 387 keyfile = self.auth['key']
384 388 certfile = self.auth['cert']
385 389
386 390 conn = httpsconnection(host, port, keyfile, certfile, *args,
387 391 **kwargs)
388 392 conn.ui = self.ui
389 393 return conn
390 394
391 395 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
392 396 def __init__(self, *args, **kwargs):
393 397 urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
394 398 self.retried_req = None
395 399
396 400 def reset_retry_count(self):
397 401 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
398 402 # forever. We disable reset_retry_count completely and reset in
399 403 # http_error_auth_reqed instead.
400 404 pass
401 405
402 406 def http_error_auth_reqed(self, auth_header, host, req, headers):
403 407 # Reset the retry counter once for each request.
404 408 if req is not self.retried_req:
405 409 self.retried_req = req
406 410 self.retried = 0
407 411 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
408 412 # it doesn't know about the auth type requested. This can happen if
409 413 # somebody is using BasicAuth and types a bad password.
410 414 try:
411 415 return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
412 416 self, auth_header, host, req, headers)
413 417 except ValueError, inst:
414 418 arg = inst.args[0]
415 419 if arg.startswith("AbstractDigestAuthHandler doesn't know "):
416 420 return
417 421 raise
418 422
419 423 class httpbasicauthhandler(urllib2.HTTPBasicAuthHandler):
420 424 def __init__(self, *args, **kwargs):
421 425 urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
422 426 self.retried_req = None
423 427
424 428 def reset_retry_count(self):
425 429 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
426 430 # forever. We disable reset_retry_count completely and reset in
427 431 # http_error_auth_reqed instead.
428 432 pass
429 433
430 434 def http_error_auth_reqed(self, auth_header, host, req, headers):
431 435 # Reset the retry counter once for each request.
432 436 if req is not self.retried_req:
433 437 self.retried_req = req
434 438 self.retried = 0
435 439 return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
436 440 self, auth_header, host, req, headers)
437 441
438 442 handlerfuncs = []
439 443
440 444 def opener(ui, authinfo=None):
441 445 '''
442 446 construct an opener suitable for urllib2
443 447 authinfo will be added to the password manager
444 448 '''
445 449 if ui.configbool('ui', 'usehttp2', False):
446 450 handlers = [httpconnectionmod.http2handler(ui, passwordmgr(ui))]
447 451 else:
448 452 handlers = [httphandler()]
449 453 if has_https:
450 454 handlers.append(httpshandler(ui))
451 455
452 456 handlers.append(proxyhandler(ui))
453 457
454 458 passmgr = passwordmgr(ui)
455 459 if authinfo is not None:
456 460 passmgr.add_password(*authinfo)
457 461 user, passwd = authinfo[2:4]
458 462 ui.debug('http auth: user %s, password %s\n' %
459 463 (user, passwd and '*' * len(passwd) or 'not set'))
460 464
461 465 handlers.extend((httpbasicauthhandler(passmgr),
462 466 httpdigestauthhandler(passmgr)))
463 467 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
464 468 opener = urllib2.build_opener(*handlers)
465 469
466 470 # 1.0 here is the _protocol_ version
467 471 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
468 472 opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
469 473 return opener
470 474
471 475 def open(ui, url_, data=None):
472 476 u = util.url(url_)
473 477 if u.scheme:
474 478 u.scheme = u.scheme.lower()
475 479 url_, authinfo = u.authinfo()
476 480 else:
477 481 path = util.normpath(os.path.abspath(url_))
478 482 url_ = 'file://' + urllib.pathname2url(path)
479 483 authinfo = None
480 484 return opener(ui, authinfo).open(url_, data)
@@ -1,225 +1,237 b''
1 1 $ "$TESTDIR/hghave" serve || exit 80
2 2
3 3 $ hg init test
4 4 $ cd test
5 5 $ echo foo>foo
6 6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
7 7 $ echo foo>foo.d/foo
8 8 $ echo bar>foo.d/bAr.hg.d/BaR
9 9 $ echo bar>foo.d/baR.d.hg/bAR
10 10 $ hg commit -A -m 1
11 11 adding foo
12 12 adding foo.d/bAr.hg.d/BaR
13 13 adding foo.d/baR.d.hg/bAR
14 14 adding foo.d/foo
15 15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
16 16 $ hg --config server.uncompressed=False serve -p $HGPORT1 -d --pid-file=../hg2.pid
17 17
18 18 Test server address cannot be reused
19 19
20 20 #if windows
21 21 $ hg serve -p $HGPORT1 2>&1
22 22 abort: cannot start server at ':$HGPORT1': * (glob)
23 23 [255]
24 24 #else
25 25 $ hg serve -p $HGPORT1 2>&1
26 26 abort: cannot start server at ':$HGPORT1': Address already in use
27 27 [255]
28 28 #endif
29 29 $ cd ..
30 30 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
31 31
32 32 clone via stream
33 33
34 34 $ hg clone --uncompressed http://localhost:$HGPORT/ copy 2>&1
35 35 streaming all changes
36 36 6 files to transfer, 606 bytes of data
37 37 transferred * bytes in * seconds (*/sec) (glob)
38 38 updating to branch default
39 39 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 40 $ hg verify -R copy
41 41 checking changesets
42 42 checking manifests
43 43 crosschecking files in changesets and manifests
44 44 checking files
45 45 4 files, 1 changesets, 4 total revisions
46 46
47 47 try to clone via stream, should use pull instead
48 48
49 49 $ hg clone --uncompressed http://localhost:$HGPORT1/ copy2
50 50 requesting all changes
51 51 adding changesets
52 52 adding manifests
53 53 adding file changes
54 54 added 1 changesets with 4 changes to 4 files
55 55 updating to branch default
56 56 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 57
58 58 clone via pull
59 59
60 60 $ hg clone http://localhost:$HGPORT1/ copy-pull
61 61 requesting all changes
62 62 adding changesets
63 63 adding manifests
64 64 adding file changes
65 65 added 1 changesets with 4 changes to 4 files
66 66 updating to branch default
67 67 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 68 $ hg verify -R copy-pull
69 69 checking changesets
70 70 checking manifests
71 71 crosschecking files in changesets and manifests
72 72 checking files
73 73 4 files, 1 changesets, 4 total revisions
74 74 $ cd test
75 75 $ echo bar > bar
76 76 $ hg commit -A -d '1 0' -m 2
77 77 adding bar
78 78 $ cd ..
79 79
80 80 clone over http with --update
81 81
82 82 $ hg clone http://localhost:$HGPORT1/ updated --update 0
83 83 requesting all changes
84 84 adding changesets
85 85 adding manifests
86 86 adding file changes
87 87 added 2 changesets with 5 changes to 5 files
88 88 updating to branch default
89 89 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90 $ hg log -r . -R updated
91 91 changeset: 0:8b6053c928fe
92 92 user: test
93 93 date: Thu Jan 01 00:00:00 1970 +0000
94 94 summary: 1
95 95
96 96 $ rm -rf updated
97 97
98 98 incoming via HTTP
99 99
100 100 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
101 101 adding changesets
102 102 adding manifests
103 103 adding file changes
104 104 added 1 changesets with 4 changes to 4 files
105 105 updating to branch default
106 106 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 107 $ cd partial
108 108 $ touch LOCAL
109 109 $ hg ci -qAm LOCAL
110 110 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
111 111 comparing with http://localhost:$HGPORT1/
112 112 searching for changes
113 113 2
114 114 $ cd ..
115 115
116 116 pull
117 117
118 118 $ cd copy-pull
119 119 $ echo '[hooks]' >> .hg/hgrc
120 120 $ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup" >> .hg/hgrc
121 121 $ hg pull
122 122 pulling from http://localhost:$HGPORT1/
123 123 searching for changes
124 124 adding changesets
125 125 adding manifests
126 126 adding file changes
127 127 added 1 changesets with 1 changes to 1 files
128 128 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=http://localhost:$HGPORT1/
129 129 (run 'hg update' to get a working copy)
130 130 $ cd ..
131 131
132 132 clone from invalid URL
133 133
134 134 $ hg clone http://localhost:$HGPORT/bad
135 135 abort: HTTP Error 404: Not Found
136 136 [255]
137 137
138 138 test http authentication
139 139 + use the same server to test server side streaming preference
140 140
141 141 $ cd test
142 142 $ cat << EOT > userpass.py
143 143 > import base64
144 144 > from mercurial.hgweb import common
145 145 > def perform_authentication(hgweb, req, op):
146 146 > auth = req.env.get('HTTP_AUTHORIZATION')
147 147 > if not auth:
148 148 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
149 149 > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
150 150 > if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']:
151 151 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no')
152 152 > def extsetup():
153 153 > common.permhooks.insert(0, perform_authentication)
154 154 > EOT
155 155 $ hg --config extensions.x=userpass.py serve -p $HGPORT2 -d --pid-file=pid \
156 156 > --config server.preferuncompressed=True
157 157 $ cat pid >> $DAEMON_PIDS
158 158
159 $ cat << EOF > get_pass.py
160 > import getpass
161 > def newgetpass(arg):
162 > return "pass"
163 > getpass.getpass = newgetpass
164 > EOF
165
159 166 $ hg id http://localhost:$HGPORT2/
160 abort: http authorization required
167 abort: http authorization required for http://localhost:$HGPORT2/
161 168 [255]
162 $ hg id http://user@localhost:$HGPORT2/
163 abort: http authorization required
169 $ hg id http://localhost:$HGPORT2/
170 abort: http authorization required for http://localhost:$HGPORT2/
164 171 [255]
172 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
173 http authorization required for http://localhost:$HGPORT2/
174 realm: mercurial
175 user: user
176 password: 5fed3813f7f5
165 177 $ hg id http://user:pass@localhost:$HGPORT2/
166 178 5fed3813f7f5
167 179 $ echo '[auth]' >> .hg/hgrc
168 180 $ echo 'l.schemes=http' >> .hg/hgrc
169 181 $ echo 'l.prefix=lo' >> .hg/hgrc
170 182 $ echo 'l.username=user' >> .hg/hgrc
171 183 $ echo 'l.password=pass' >> .hg/hgrc
172 184 $ hg id http://localhost:$HGPORT2/
173 185 5fed3813f7f5
174 186 $ hg id http://localhost:$HGPORT2/
175 187 5fed3813f7f5
176 188 $ hg id http://user@localhost:$HGPORT2/
177 189 5fed3813f7f5
178 190 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
179 191 streaming all changes
180 192 7 files to transfer, 916 bytes of data
181 193 transferred * bytes in * seconds (*/sec) (glob)
182 194 updating to branch default
183 195 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 196
185 197 $ hg id http://user2@localhost:$HGPORT2/
186 abort: http authorization required
198 abort: http authorization required for http://localhost:$HGPORT2/
187 199 [255]
188 200 $ hg id http://user:pass2@localhost:$HGPORT2/
189 201 abort: HTTP Error 403: no
190 202 [255]
191 203
192 204 $ cd ..
193 205
194 206 clone of serve with repo in root and unserved subrepo (issue2970)
195 207
196 208 $ hg --cwd test init sub
197 209 $ echo empty > test/sub/empty
198 210 $ hg --cwd test/sub add empty
199 211 $ hg --cwd test/sub commit -qm 'add empty'
200 212 $ hg --cwd test/sub tag -r 0 something
201 213 $ echo sub = sub > test/.hgsub
202 214 $ hg --cwd test add .hgsub
203 215 $ hg --cwd test commit -qm 'add subrepo'
204 216 $ hg clone http://localhost:$HGPORT noslash-clone
205 217 requesting all changes
206 218 adding changesets
207 219 adding manifests
208 220 adding file changes
209 221 added 3 changesets with 7 changes to 7 files
210 222 updating to branch default
211 223 abort: HTTP Error 404: Not Found
212 224 [255]
213 225 $ hg clone http://localhost:$HGPORT/ slash-clone
214 226 requesting all changes
215 227 adding changesets
216 228 adding manifests
217 229 adding file changes
218 230 added 3 changesets with 7 changes to 7 files
219 231 updating to branch default
220 232 abort: HTTP Error 404: Not Found
221 233 [255]
222 234
223 235 check error log
224 236
225 237 $ cat error.log
General Comments 0
You need to be logged in to leave comments. Login now