##// END OF EJS Templates
py3: ensure the HTTP password manager returns strings, not bytes...
Matt Harbison -
r41730:349c8879 default
parent child Browse files
Show More
@@ -1,642 +1,642 b''
1 # url.py - HTTP handling for mercurial
1 # url.py - HTTP handling for mercurial
2 #
2 #
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import base64
12 import base64
13 import os
13 import os
14 import socket
14 import socket
15 import sys
15 import sys
16
16
17 from .i18n import _
17 from .i18n import _
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 error,
20 error,
21 httpconnection as httpconnectionmod,
21 httpconnection as httpconnectionmod,
22 keepalive,
22 keepalive,
23 pycompat,
23 pycompat,
24 sslutil,
24 sslutil,
25 urllibcompat,
25 urllibcompat,
26 util,
26 util,
27 )
27 )
28 from .utils import (
28 from .utils import (
29 stringutil,
29 stringutil,
30 )
30 )
31
31
32 httplib = util.httplib
32 httplib = util.httplib
33 stringio = util.stringio
33 stringio = util.stringio
34 urlerr = util.urlerr
34 urlerr = util.urlerr
35 urlreq = util.urlreq
35 urlreq = util.urlreq
36
36
37 def escape(s, quote=None):
37 def escape(s, quote=None):
38 '''Replace special characters "&", "<" and ">" to HTML-safe sequences.
38 '''Replace special characters "&", "<" and ">" to HTML-safe sequences.
39 If the optional flag quote is true, the quotation mark character (")
39 If the optional flag quote is true, the quotation mark character (")
40 is also translated.
40 is also translated.
41
41
42 This is the same as cgi.escape in Python, but always operates on
42 This is the same as cgi.escape in Python, but always operates on
43 bytes, whereas cgi.escape in Python 3 only works on unicodes.
43 bytes, whereas cgi.escape in Python 3 only works on unicodes.
44 '''
44 '''
45 s = s.replace(b"&", b"&amp;")
45 s = s.replace(b"&", b"&amp;")
46 s = s.replace(b"<", b"&lt;")
46 s = s.replace(b"<", b"&lt;")
47 s = s.replace(b">", b"&gt;")
47 s = s.replace(b">", b"&gt;")
48 if quote:
48 if quote:
49 s = s.replace(b'"', b"&quot;")
49 s = s.replace(b'"', b"&quot;")
50 return s
50 return s
51
51
52 class passwordmgr(object):
52 class passwordmgr(object):
53 def __init__(self, ui, passwddb):
53 def __init__(self, ui, passwddb):
54 self.ui = ui
54 self.ui = ui
55 self.passwddb = passwddb
55 self.passwddb = passwddb
56
56
57 def add_password(self, realm, uri, user, passwd):
57 def add_password(self, realm, uri, user, passwd):
58 return self.passwddb.add_password(realm, uri, user, passwd)
58 return self.passwddb.add_password(realm, uri, user, passwd)
59
59
60 def find_user_password(self, realm, authuri):
60 def find_user_password(self, realm, authuri):
61 assert isinstance(realm, (type(None), str))
61 assert isinstance(realm, (type(None), str))
62 assert isinstance(authuri, str)
62 assert isinstance(authuri, str)
63 authinfo = self.passwddb.find_user_password(realm, authuri)
63 authinfo = self.passwddb.find_user_password(realm, authuri)
64 user, passwd = authinfo
64 user, passwd = authinfo
65 user, passwd = pycompat.bytesurl(user), pycompat.bytesurl(passwd)
65 user, passwd = pycompat.bytesurl(user), pycompat.bytesurl(passwd)
66 if user and passwd:
66 if user and passwd:
67 self._writedebug(user, passwd)
67 self._writedebug(user, passwd)
68 return (user, passwd)
68 return (pycompat.strurl(user), pycompat.strurl(passwd))
69
69
70 if not user or not passwd:
70 if not user or not passwd:
71 res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
71 res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
72 if res:
72 if res:
73 group, auth = res
73 group, auth = res
74 user, passwd = auth.get('username'), auth.get('password')
74 user, passwd = auth.get('username'), auth.get('password')
75 self.ui.debug("using auth.%s.* for authentication\n" % group)
75 self.ui.debug("using auth.%s.* for authentication\n" % group)
76 if not user or not passwd:
76 if not user or not passwd:
77 u = util.url(pycompat.bytesurl(authuri))
77 u = util.url(pycompat.bytesurl(authuri))
78 u.query = None
78 u.query = None
79 if not self.ui.interactive():
79 if not self.ui.interactive():
80 raise error.Abort(_('http authorization required for %s') %
80 raise error.Abort(_('http authorization required for %s') %
81 util.hidepassword(bytes(u)))
81 util.hidepassword(bytes(u)))
82
82
83 self.ui.write(_("http authorization required for %s\n") %
83 self.ui.write(_("http authorization required for %s\n") %
84 util.hidepassword(bytes(u)))
84 util.hidepassword(bytes(u)))
85 self.ui.write(_("realm: %s\n") % pycompat.bytesurl(realm))
85 self.ui.write(_("realm: %s\n") % pycompat.bytesurl(realm))
86 if user:
86 if user:
87 self.ui.write(_("user: %s\n") % user)
87 self.ui.write(_("user: %s\n") % user)
88 else:
88 else:
89 user = self.ui.prompt(_("user:"), default=None)
89 user = self.ui.prompt(_("user:"), default=None)
90
90
91 if not passwd:
91 if not passwd:
92 passwd = self.ui.getpass()
92 passwd = self.ui.getpass()
93
93
94 self.passwddb.add_password(realm, authuri, user, passwd)
94 self.passwddb.add_password(realm, authuri, user, passwd)
95 self._writedebug(user, passwd)
95 self._writedebug(user, passwd)
96 return (user, passwd)
96 return (pycompat.strurl(user), pycompat.strurl(passwd))
97
97
98 def _writedebug(self, user, passwd):
98 def _writedebug(self, user, passwd):
99 msg = _('http auth: user %s, password %s\n')
99 msg = _('http auth: user %s, password %s\n')
100 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
100 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
101
101
102 def find_stored_password(self, authuri):
102 def find_stored_password(self, authuri):
103 return self.passwddb.find_user_password(None, authuri)
103 return self.passwddb.find_user_password(None, authuri)
104
104
105 class proxyhandler(urlreq.proxyhandler):
105 class proxyhandler(urlreq.proxyhandler):
106 def __init__(self, ui):
106 def __init__(self, ui):
107 proxyurl = (ui.config("http_proxy", "host") or
107 proxyurl = (ui.config("http_proxy", "host") or
108 encoding.environ.get('http_proxy'))
108 encoding.environ.get('http_proxy'))
109 # XXX proxyauthinfo = None
109 # XXX proxyauthinfo = None
110
110
111 if proxyurl:
111 if proxyurl:
112 # proxy can be proper url or host[:port]
112 # proxy can be proper url or host[:port]
113 if not (proxyurl.startswith('http:') or
113 if not (proxyurl.startswith('http:') or
114 proxyurl.startswith('https:')):
114 proxyurl.startswith('https:')):
115 proxyurl = 'http://' + proxyurl + '/'
115 proxyurl = 'http://' + proxyurl + '/'
116 proxy = util.url(proxyurl)
116 proxy = util.url(proxyurl)
117 if not proxy.user:
117 if not proxy.user:
118 proxy.user = ui.config("http_proxy", "user")
118 proxy.user = ui.config("http_proxy", "user")
119 proxy.passwd = ui.config("http_proxy", "passwd")
119 proxy.passwd = ui.config("http_proxy", "passwd")
120
120
121 # see if we should use a proxy for this url
121 # see if we should use a proxy for this url
122 no_list = ["localhost", "127.0.0.1"]
122 no_list = ["localhost", "127.0.0.1"]
123 no_list.extend([p.lower() for
123 no_list.extend([p.lower() for
124 p in ui.configlist("http_proxy", "no")])
124 p in ui.configlist("http_proxy", "no")])
125 no_list.extend([p.strip().lower() for
125 no_list.extend([p.strip().lower() for
126 p in encoding.environ.get("no_proxy", '').split(',')
126 p in encoding.environ.get("no_proxy", '').split(',')
127 if p.strip()])
127 if p.strip()])
128 # "http_proxy.always" config is for running tests on localhost
128 # "http_proxy.always" config is for running tests on localhost
129 if ui.configbool("http_proxy", "always"):
129 if ui.configbool("http_proxy", "always"):
130 self.no_list = []
130 self.no_list = []
131 else:
131 else:
132 self.no_list = no_list
132 self.no_list = no_list
133
133
134 proxyurl = bytes(proxy)
134 proxyurl = bytes(proxy)
135 proxies = {'http': proxyurl, 'https': proxyurl}
135 proxies = {'http': proxyurl, 'https': proxyurl}
136 ui.debug('proxying through %s\n' % util.hidepassword(proxyurl))
136 ui.debug('proxying through %s\n' % util.hidepassword(proxyurl))
137 else:
137 else:
138 proxies = {}
138 proxies = {}
139
139
140 urlreq.proxyhandler.__init__(self, proxies)
140 urlreq.proxyhandler.__init__(self, proxies)
141 self.ui = ui
141 self.ui = ui
142
142
143 def proxy_open(self, req, proxy, type_):
143 def proxy_open(self, req, proxy, type_):
144 host = urllibcompat.gethost(req).split(':')[0]
144 host = urllibcompat.gethost(req).split(':')[0]
145 for e in self.no_list:
145 for e in self.no_list:
146 if host == e:
146 if host == e:
147 return None
147 return None
148 if e.startswith('*.') and host.endswith(e[2:]):
148 if e.startswith('*.') and host.endswith(e[2:]):
149 return None
149 return None
150 if e.startswith('.') and host.endswith(e[1:]):
150 if e.startswith('.') and host.endswith(e[1:]):
151 return None
151 return None
152
152
153 return urlreq.proxyhandler.proxy_open(self, req, proxy, type_)
153 return urlreq.proxyhandler.proxy_open(self, req, proxy, type_)
154
154
155 def _gen_sendfile(orgsend):
155 def _gen_sendfile(orgsend):
156 def _sendfile(self, data):
156 def _sendfile(self, data):
157 # send a file
157 # send a file
158 if isinstance(data, httpconnectionmod.httpsendfile):
158 if isinstance(data, httpconnectionmod.httpsendfile):
159 # if auth required, some data sent twice, so rewind here
159 # if auth required, some data sent twice, so rewind here
160 data.seek(0)
160 data.seek(0)
161 for chunk in util.filechunkiter(data):
161 for chunk in util.filechunkiter(data):
162 orgsend(self, chunk)
162 orgsend(self, chunk)
163 else:
163 else:
164 orgsend(self, data)
164 orgsend(self, data)
165 return _sendfile
165 return _sendfile
166
166
167 has_https = util.safehasattr(urlreq, 'httpshandler')
167 has_https = util.safehasattr(urlreq, 'httpshandler')
168
168
169 class httpconnection(keepalive.HTTPConnection):
169 class httpconnection(keepalive.HTTPConnection):
170 # must be able to send big bundle as stream.
170 # must be able to send big bundle as stream.
171 send = _gen_sendfile(keepalive.HTTPConnection.send)
171 send = _gen_sendfile(keepalive.HTTPConnection.send)
172
172
173 def getresponse(self):
173 def getresponse(self):
174 proxyres = getattr(self, 'proxyres', None)
174 proxyres = getattr(self, 'proxyres', None)
175 if proxyres:
175 if proxyres:
176 if proxyres.will_close:
176 if proxyres.will_close:
177 self.close()
177 self.close()
178 self.proxyres = None
178 self.proxyres = None
179 return proxyres
179 return proxyres
180 return keepalive.HTTPConnection.getresponse(self)
180 return keepalive.HTTPConnection.getresponse(self)
181
181
182 # general transaction handler to support different ways to handle
182 # general transaction handler to support different ways to handle
183 # HTTPS proxying before and after Python 2.6.3.
183 # HTTPS proxying before and after Python 2.6.3.
184 def _generic_start_transaction(handler, h, req):
184 def _generic_start_transaction(handler, h, req):
185 tunnel_host = getattr(req, '_tunnel_host', None)
185 tunnel_host = getattr(req, '_tunnel_host', None)
186 if tunnel_host:
186 if tunnel_host:
187 if tunnel_host[:7] not in ['http://', 'https:/']:
187 if tunnel_host[:7] not in ['http://', 'https:/']:
188 tunnel_host = 'https://' + tunnel_host
188 tunnel_host = 'https://' + tunnel_host
189 new_tunnel = True
189 new_tunnel = True
190 else:
190 else:
191 tunnel_host = urllibcompat.getselector(req)
191 tunnel_host = urllibcompat.getselector(req)
192 new_tunnel = False
192 new_tunnel = False
193
193
194 if new_tunnel or tunnel_host == urllibcompat.getfullurl(req): # has proxy
194 if new_tunnel or tunnel_host == urllibcompat.getfullurl(req): # has proxy
195 u = util.url(tunnel_host)
195 u = util.url(tunnel_host)
196 if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
196 if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
197 h.realhostport = ':'.join([u.host, (u.port or '443')])
197 h.realhostport = ':'.join([u.host, (u.port or '443')])
198 h.headers = req.headers.copy()
198 h.headers = req.headers.copy()
199 h.headers.update(handler.parent.addheaders)
199 h.headers.update(handler.parent.addheaders)
200 return
200 return
201
201
202 h.realhostport = None
202 h.realhostport = None
203 h.headers = None
203 h.headers = None
204
204
205 def _generic_proxytunnel(self):
205 def _generic_proxytunnel(self):
206 proxyheaders = dict(
206 proxyheaders = dict(
207 [(x, self.headers[x]) for x in self.headers
207 [(x, self.headers[x]) for x in self.headers
208 if x.lower().startswith('proxy-')])
208 if x.lower().startswith('proxy-')])
209 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
209 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
210 for header in proxyheaders.iteritems():
210 for header in proxyheaders.iteritems():
211 self.send('%s: %s\r\n' % header)
211 self.send('%s: %s\r\n' % header)
212 self.send('\r\n')
212 self.send('\r\n')
213
213
214 # majority of the following code is duplicated from
214 # majority of the following code is duplicated from
215 # httplib.HTTPConnection as there are no adequate places to
215 # httplib.HTTPConnection as there are no adequate places to
216 # override functions to provide the needed functionality
216 # override functions to provide the needed functionality
217 res = self.response_class(self.sock,
217 res = self.response_class(self.sock,
218 strict=self.strict,
218 strict=self.strict,
219 method=self._method)
219 method=self._method)
220
220
221 while True:
221 while True:
222 version, status, reason = res._read_status()
222 version, status, reason = res._read_status()
223 if status != httplib.CONTINUE:
223 if status != httplib.CONTINUE:
224 break
224 break
225 # skip lines that are all whitespace
225 # skip lines that are all whitespace
226 list(iter(lambda: res.fp.readline().strip(), ''))
226 list(iter(lambda: res.fp.readline().strip(), ''))
227 res.status = status
227 res.status = status
228 res.reason = reason.strip()
228 res.reason = reason.strip()
229
229
230 if res.status == 200:
230 if res.status == 200:
231 # skip lines until we find a blank line
231 # skip lines until we find a blank line
232 list(iter(res.fp.readline, '\r\n'))
232 list(iter(res.fp.readline, '\r\n'))
233 return True
233 return True
234
234
235 if version == 'HTTP/1.0':
235 if version == 'HTTP/1.0':
236 res.version = 10
236 res.version = 10
237 elif version.startswith('HTTP/1.'):
237 elif version.startswith('HTTP/1.'):
238 res.version = 11
238 res.version = 11
239 elif version == 'HTTP/0.9':
239 elif version == 'HTTP/0.9':
240 res.version = 9
240 res.version = 9
241 else:
241 else:
242 raise httplib.UnknownProtocol(version)
242 raise httplib.UnknownProtocol(version)
243
243
244 if res.version == 9:
244 if res.version == 9:
245 res.length = None
245 res.length = None
246 res.chunked = 0
246 res.chunked = 0
247 res.will_close = 1
247 res.will_close = 1
248 res.msg = httplib.HTTPMessage(stringio())
248 res.msg = httplib.HTTPMessage(stringio())
249 return False
249 return False
250
250
251 res.msg = httplib.HTTPMessage(res.fp)
251 res.msg = httplib.HTTPMessage(res.fp)
252 res.msg.fp = None
252 res.msg.fp = None
253
253
254 # are we using the chunked-style of transfer encoding?
254 # are we using the chunked-style of transfer encoding?
255 trenc = res.msg.getheader('transfer-encoding')
255 trenc = res.msg.getheader('transfer-encoding')
256 if trenc and trenc.lower() == "chunked":
256 if trenc and trenc.lower() == "chunked":
257 res.chunked = 1
257 res.chunked = 1
258 res.chunk_left = None
258 res.chunk_left = None
259 else:
259 else:
260 res.chunked = 0
260 res.chunked = 0
261
261
262 # will the connection close at the end of the response?
262 # will the connection close at the end of the response?
263 res.will_close = res._check_close()
263 res.will_close = res._check_close()
264
264
265 # do we have a Content-Length?
265 # do we have a Content-Length?
266 # NOTE: RFC 2616, section 4.4, #3 says we ignore this if
266 # NOTE: RFC 2616, section 4.4, #3 says we ignore this if
267 # transfer-encoding is "chunked"
267 # transfer-encoding is "chunked"
268 length = res.msg.getheader('content-length')
268 length = res.msg.getheader('content-length')
269 if length and not res.chunked:
269 if length and not res.chunked:
270 try:
270 try:
271 res.length = int(length)
271 res.length = int(length)
272 except ValueError:
272 except ValueError:
273 res.length = None
273 res.length = None
274 else:
274 else:
275 if res.length < 0: # ignore nonsensical negative lengths
275 if res.length < 0: # ignore nonsensical negative lengths
276 res.length = None
276 res.length = None
277 else:
277 else:
278 res.length = None
278 res.length = None
279
279
280 # does the body have a fixed length? (of zero)
280 # does the body have a fixed length? (of zero)
281 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
281 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
282 100 <= status < 200 or # 1xx codes
282 100 <= status < 200 or # 1xx codes
283 res._method == 'HEAD'):
283 res._method == 'HEAD'):
284 res.length = 0
284 res.length = 0
285
285
286 # if the connection remains open, and we aren't using chunked, and
286 # if the connection remains open, and we aren't using chunked, and
287 # a content-length was not provided, then assume that the connection
287 # a content-length was not provided, then assume that the connection
288 # WILL close.
288 # WILL close.
289 if (not res.will_close and
289 if (not res.will_close and
290 not res.chunked and
290 not res.chunked and
291 res.length is None):
291 res.length is None):
292 res.will_close = 1
292 res.will_close = 1
293
293
294 self.proxyres = res
294 self.proxyres = res
295
295
296 return False
296 return False
297
297
298 class httphandler(keepalive.HTTPHandler):
298 class httphandler(keepalive.HTTPHandler):
299 def http_open(self, req):
299 def http_open(self, req):
300 return self.do_open(httpconnection, req)
300 return self.do_open(httpconnection, req)
301
301
302 def _start_transaction(self, h, req):
302 def _start_transaction(self, h, req):
303 _generic_start_transaction(self, h, req)
303 _generic_start_transaction(self, h, req)
304 return keepalive.HTTPHandler._start_transaction(self, h, req)
304 return keepalive.HTTPHandler._start_transaction(self, h, req)
305
305
306 class logginghttpconnection(keepalive.HTTPConnection):
306 class logginghttpconnection(keepalive.HTTPConnection):
307 def __init__(self, createconn, *args, **kwargs):
307 def __init__(self, createconn, *args, **kwargs):
308 keepalive.HTTPConnection.__init__(self, *args, **kwargs)
308 keepalive.HTTPConnection.__init__(self, *args, **kwargs)
309 self._create_connection = createconn
309 self._create_connection = createconn
310
310
311 if sys.version_info < (2, 7, 7):
311 if sys.version_info < (2, 7, 7):
312 # copied from 2.7.14, since old implementations directly call
312 # copied from 2.7.14, since old implementations directly call
313 # socket.create_connection()
313 # socket.create_connection()
314 def connect(self):
314 def connect(self):
315 self.sock = self._create_connection((self.host, self.port),
315 self.sock = self._create_connection((self.host, self.port),
316 self.timeout,
316 self.timeout,
317 self.source_address)
317 self.source_address)
318 if self._tunnel_host:
318 if self._tunnel_host:
319 self._tunnel()
319 self._tunnel()
320
320
321 class logginghttphandler(httphandler):
321 class logginghttphandler(httphandler):
322 """HTTP handler that logs socket I/O."""
322 """HTTP handler that logs socket I/O."""
323 def __init__(self, logfh, name, observeropts, timeout=None):
323 def __init__(self, logfh, name, observeropts, timeout=None):
324 super(logginghttphandler, self).__init__(timeout=timeout)
324 super(logginghttphandler, self).__init__(timeout=timeout)
325
325
326 self._logfh = logfh
326 self._logfh = logfh
327 self._logname = name
327 self._logname = name
328 self._observeropts = observeropts
328 self._observeropts = observeropts
329
329
330 # do_open() calls the passed class to instantiate an HTTPConnection. We
330 # do_open() calls the passed class to instantiate an HTTPConnection. We
331 # pass in a callable method that creates a custom HTTPConnection instance
331 # pass in a callable method that creates a custom HTTPConnection instance
332 # whose callback to create the socket knows how to proxy the socket.
332 # whose callback to create the socket knows how to proxy the socket.
333 def http_open(self, req):
333 def http_open(self, req):
334 return self.do_open(self._makeconnection, req)
334 return self.do_open(self._makeconnection, req)
335
335
336 def _makeconnection(self, *args, **kwargs):
336 def _makeconnection(self, *args, **kwargs):
337 def createconnection(*args, **kwargs):
337 def createconnection(*args, **kwargs):
338 sock = socket.create_connection(*args, **kwargs)
338 sock = socket.create_connection(*args, **kwargs)
339 return util.makeloggingsocket(self._logfh, sock, self._logname,
339 return util.makeloggingsocket(self._logfh, sock, self._logname,
340 **self._observeropts)
340 **self._observeropts)
341
341
342 return logginghttpconnection(createconnection, *args, **kwargs)
342 return logginghttpconnection(createconnection, *args, **kwargs)
343
343
344 if has_https:
344 if has_https:
345 class httpsconnection(keepalive.HTTPConnection):
345 class httpsconnection(keepalive.HTTPConnection):
346 response_class = keepalive.HTTPResponse
346 response_class = keepalive.HTTPResponse
347 default_port = httplib.HTTPS_PORT
347 default_port = httplib.HTTPS_PORT
348 # must be able to send big bundle as stream.
348 # must be able to send big bundle as stream.
349 send = _gen_sendfile(keepalive.safesend)
349 send = _gen_sendfile(keepalive.safesend)
350 getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection)
350 getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection)
351
351
352 def __init__(self, host, port=None, key_file=None, cert_file=None,
352 def __init__(self, host, port=None, key_file=None, cert_file=None,
353 *args, **kwargs):
353 *args, **kwargs):
354 keepalive.HTTPConnection.__init__(self, host, port, *args, **kwargs)
354 keepalive.HTTPConnection.__init__(self, host, port, *args, **kwargs)
355 self.key_file = key_file
355 self.key_file = key_file
356 self.cert_file = cert_file
356 self.cert_file = cert_file
357
357
358 def connect(self):
358 def connect(self):
359 self.sock = socket.create_connection((self.host, self.port))
359 self.sock = socket.create_connection((self.host, self.port))
360
360
361 host = self.host
361 host = self.host
362 if self.realhostport: # use CONNECT proxy
362 if self.realhostport: # use CONNECT proxy
363 _generic_proxytunnel(self)
363 _generic_proxytunnel(self)
364 host = self.realhostport.rsplit(':', 1)[0]
364 host = self.realhostport.rsplit(':', 1)[0]
365 self.sock = sslutil.wrapsocket(
365 self.sock = sslutil.wrapsocket(
366 self.sock, self.key_file, self.cert_file, ui=self.ui,
366 self.sock, self.key_file, self.cert_file, ui=self.ui,
367 serverhostname=host)
367 serverhostname=host)
368 sslutil.validatesocket(self.sock)
368 sslutil.validatesocket(self.sock)
369
369
370 class httpshandler(keepalive.KeepAliveHandler, urlreq.httpshandler):
370 class httpshandler(keepalive.KeepAliveHandler, urlreq.httpshandler):
371 def __init__(self, ui, timeout=None):
371 def __init__(self, ui, timeout=None):
372 keepalive.KeepAliveHandler.__init__(self, timeout=timeout)
372 keepalive.KeepAliveHandler.__init__(self, timeout=timeout)
373 urlreq.httpshandler.__init__(self)
373 urlreq.httpshandler.__init__(self)
374 self.ui = ui
374 self.ui = ui
375 self.pwmgr = passwordmgr(self.ui,
375 self.pwmgr = passwordmgr(self.ui,
376 self.ui.httppasswordmgrdb)
376 self.ui.httppasswordmgrdb)
377
377
378 def _start_transaction(self, h, req):
378 def _start_transaction(self, h, req):
379 _generic_start_transaction(self, h, req)
379 _generic_start_transaction(self, h, req)
380 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
380 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
381
381
382 def https_open(self, req):
382 def https_open(self, req):
383 # urllibcompat.getfullurl() does not contain credentials
383 # urllibcompat.getfullurl() does not contain credentials
384 # and we may need them to match the certificates.
384 # and we may need them to match the certificates.
385 url = urllibcompat.getfullurl(req)
385 url = urllibcompat.getfullurl(req)
386 user, password = self.pwmgr.find_stored_password(url)
386 user, password = self.pwmgr.find_stored_password(url)
387 res = httpconnectionmod.readauthforuri(self.ui, url, user)
387 res = httpconnectionmod.readauthforuri(self.ui, url, user)
388 if res:
388 if res:
389 group, auth = res
389 group, auth = res
390 self.auth = auth
390 self.auth = auth
391 self.ui.debug("using auth.%s.* for authentication\n" % group)
391 self.ui.debug("using auth.%s.* for authentication\n" % group)
392 else:
392 else:
393 self.auth = None
393 self.auth = None
394 return self.do_open(self._makeconnection, req)
394 return self.do_open(self._makeconnection, req)
395
395
396 def _makeconnection(self, host, port=None, *args, **kwargs):
396 def _makeconnection(self, host, port=None, *args, **kwargs):
397 keyfile = None
397 keyfile = None
398 certfile = None
398 certfile = None
399
399
400 if len(args) >= 1: # key_file
400 if len(args) >= 1: # key_file
401 keyfile = args[0]
401 keyfile = args[0]
402 if len(args) >= 2: # cert_file
402 if len(args) >= 2: # cert_file
403 certfile = args[1]
403 certfile = args[1]
404 args = args[2:]
404 args = args[2:]
405
405
406 # if the user has specified different key/cert files in
406 # if the user has specified different key/cert files in
407 # hgrc, we prefer these
407 # hgrc, we prefer these
408 if self.auth and 'key' in self.auth and 'cert' in self.auth:
408 if self.auth and 'key' in self.auth and 'cert' in self.auth:
409 keyfile = self.auth['key']
409 keyfile = self.auth['key']
410 certfile = self.auth['cert']
410 certfile = self.auth['cert']
411
411
412 conn = httpsconnection(host, port, keyfile, certfile, *args,
412 conn = httpsconnection(host, port, keyfile, certfile, *args,
413 **kwargs)
413 **kwargs)
414 conn.ui = self.ui
414 conn.ui = self.ui
415 return conn
415 return conn
416
416
417 class httpdigestauthhandler(urlreq.httpdigestauthhandler):
417 class httpdigestauthhandler(urlreq.httpdigestauthhandler):
418 def __init__(self, *args, **kwargs):
418 def __init__(self, *args, **kwargs):
419 urlreq.httpdigestauthhandler.__init__(self, *args, **kwargs)
419 urlreq.httpdigestauthhandler.__init__(self, *args, **kwargs)
420 self.retried_req = None
420 self.retried_req = None
421
421
422 def reset_retry_count(self):
422 def reset_retry_count(self):
423 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
423 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
424 # forever. We disable reset_retry_count completely and reset in
424 # forever. We disable reset_retry_count completely and reset in
425 # http_error_auth_reqed instead.
425 # http_error_auth_reqed instead.
426 pass
426 pass
427
427
428 def http_error_auth_reqed(self, auth_header, host, req, headers):
428 def http_error_auth_reqed(self, auth_header, host, req, headers):
429 # Reset the retry counter once for each request.
429 # Reset the retry counter once for each request.
430 if req is not self.retried_req:
430 if req is not self.retried_req:
431 self.retried_req = req
431 self.retried_req = req
432 self.retried = 0
432 self.retried = 0
433 return urlreq.httpdigestauthhandler.http_error_auth_reqed(
433 return urlreq.httpdigestauthhandler.http_error_auth_reqed(
434 self, auth_header, host, req, headers)
434 self, auth_header, host, req, headers)
435
435
436 class httpbasicauthhandler(urlreq.httpbasicauthhandler):
436 class httpbasicauthhandler(urlreq.httpbasicauthhandler):
437 def __init__(self, *args, **kwargs):
437 def __init__(self, *args, **kwargs):
438 self.auth = None
438 self.auth = None
439 urlreq.httpbasicauthhandler.__init__(self, *args, **kwargs)
439 urlreq.httpbasicauthhandler.__init__(self, *args, **kwargs)
440 self.retried_req = None
440 self.retried_req = None
441
441
442 def http_request(self, request):
442 def http_request(self, request):
443 if self.auth:
443 if self.auth:
444 request.add_unredirected_header(self.auth_header, self.auth)
444 request.add_unredirected_header(self.auth_header, self.auth)
445
445
446 return request
446 return request
447
447
448 def https_request(self, request):
448 def https_request(self, request):
449 if self.auth:
449 if self.auth:
450 request.add_unredirected_header(self.auth_header, self.auth)
450 request.add_unredirected_header(self.auth_header, self.auth)
451
451
452 return request
452 return request
453
453
454 def reset_retry_count(self):
454 def reset_retry_count(self):
455 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
455 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
456 # forever. We disable reset_retry_count completely and reset in
456 # forever. We disable reset_retry_count completely and reset in
457 # http_error_auth_reqed instead.
457 # http_error_auth_reqed instead.
458 pass
458 pass
459
459
460 def http_error_auth_reqed(self, auth_header, host, req, headers):
460 def http_error_auth_reqed(self, auth_header, host, req, headers):
461 # Reset the retry counter once for each request.
461 # Reset the retry counter once for each request.
462 if req is not self.retried_req:
462 if req is not self.retried_req:
463 self.retried_req = req
463 self.retried_req = req
464 self.retried = 0
464 self.retried = 0
465 return urlreq.httpbasicauthhandler.http_error_auth_reqed(
465 return urlreq.httpbasicauthhandler.http_error_auth_reqed(
466 self, auth_header, host, req, headers)
466 self, auth_header, host, req, headers)
467
467
468 def retry_http_basic_auth(self, host, req, realm):
468 def retry_http_basic_auth(self, host, req, realm):
469 user, pw = self.passwd.find_user_password(
469 user, pw = self.passwd.find_user_password(
470 realm, urllibcompat.getfullurl(req))
470 realm, urllibcompat.getfullurl(req))
471 if pw is not None:
471 if pw is not None:
472 raw = "%s:%s" % (pycompat.bytesurl(user), pycompat.bytesurl(pw))
472 raw = "%s:%s" % (pycompat.bytesurl(user), pycompat.bytesurl(pw))
473 auth = r'Basic %s' % pycompat.strurl(base64.b64encode(raw).strip())
473 auth = r'Basic %s' % pycompat.strurl(base64.b64encode(raw).strip())
474 if req.get_header(self.auth_header, None) == auth:
474 if req.get_header(self.auth_header, None) == auth:
475 return None
475 return None
476 self.auth = auth
476 self.auth = auth
477 req.add_unredirected_header(self.auth_header, auth)
477 req.add_unredirected_header(self.auth_header, auth)
478 return self.parent.open(req)
478 return self.parent.open(req)
479 else:
479 else:
480 return None
480 return None
481
481
482 class cookiehandler(urlreq.basehandler):
482 class cookiehandler(urlreq.basehandler):
483 def __init__(self, ui):
483 def __init__(self, ui):
484 self.cookiejar = None
484 self.cookiejar = None
485
485
486 cookiefile = ui.config('auth', 'cookiefile')
486 cookiefile = ui.config('auth', 'cookiefile')
487 if not cookiefile:
487 if not cookiefile:
488 return
488 return
489
489
490 cookiefile = util.expandpath(cookiefile)
490 cookiefile = util.expandpath(cookiefile)
491 try:
491 try:
492 cookiejar = util.cookielib.MozillaCookieJar(
492 cookiejar = util.cookielib.MozillaCookieJar(
493 pycompat.fsdecode(cookiefile))
493 pycompat.fsdecode(cookiefile))
494 cookiejar.load()
494 cookiejar.load()
495 self.cookiejar = cookiejar
495 self.cookiejar = cookiejar
496 except util.cookielib.LoadError as e:
496 except util.cookielib.LoadError as e:
497 ui.warn(_('(error loading cookie file %s: %s; continuing without '
497 ui.warn(_('(error loading cookie file %s: %s; continuing without '
498 'cookies)\n') % (cookiefile, stringutil.forcebytestr(e)))
498 'cookies)\n') % (cookiefile, stringutil.forcebytestr(e)))
499
499
500 def http_request(self, request):
500 def http_request(self, request):
501 if self.cookiejar:
501 if self.cookiejar:
502 self.cookiejar.add_cookie_header(request)
502 self.cookiejar.add_cookie_header(request)
503
503
504 return request
504 return request
505
505
506 def https_request(self, request):
506 def https_request(self, request):
507 if self.cookiejar:
507 if self.cookiejar:
508 self.cookiejar.add_cookie_header(request)
508 self.cookiejar.add_cookie_header(request)
509
509
510 return request
510 return request
511
511
512 handlerfuncs = []
512 handlerfuncs = []
513
513
514 def opener(ui, authinfo=None, useragent=None, loggingfh=None,
514 def opener(ui, authinfo=None, useragent=None, loggingfh=None,
515 loggingname=b's', loggingopts=None, sendaccept=True):
515 loggingname=b's', loggingopts=None, sendaccept=True):
516 '''
516 '''
517 construct an opener suitable for urllib2
517 construct an opener suitable for urllib2
518 authinfo will be added to the password manager
518 authinfo will be added to the password manager
519
519
520 The opener can be configured to log socket events if the various
520 The opener can be configured to log socket events if the various
521 ``logging*`` arguments are specified.
521 ``logging*`` arguments are specified.
522
522
523 ``loggingfh`` denotes a file object to log events to.
523 ``loggingfh`` denotes a file object to log events to.
524 ``loggingname`` denotes the name of the to print when logging.
524 ``loggingname`` denotes the name of the to print when logging.
525 ``loggingopts`` is a dict of keyword arguments to pass to the constructed
525 ``loggingopts`` is a dict of keyword arguments to pass to the constructed
526 ``util.socketobserver`` instance.
526 ``util.socketobserver`` instance.
527
527
528 ``sendaccept`` allows controlling whether the ``Accept`` request header
528 ``sendaccept`` allows controlling whether the ``Accept`` request header
529 is sent. The header is sent by default.
529 is sent. The header is sent by default.
530 '''
530 '''
531 timeout = ui.configwith(float, 'http', 'timeout')
531 timeout = ui.configwith(float, 'http', 'timeout')
532 handlers = []
532 handlers = []
533
533
534 if loggingfh:
534 if loggingfh:
535 handlers.append(logginghttphandler(loggingfh, loggingname,
535 handlers.append(logginghttphandler(loggingfh, loggingname,
536 loggingopts or {}, timeout=timeout))
536 loggingopts or {}, timeout=timeout))
537 # We don't yet support HTTPS when logging I/O. If we attempt to open
537 # We don't yet support HTTPS when logging I/O. If we attempt to open
538 # an HTTPS URL, we'll likely fail due to unknown protocol.
538 # an HTTPS URL, we'll likely fail due to unknown protocol.
539
539
540 else:
540 else:
541 handlers.append(httphandler(timeout=timeout))
541 handlers.append(httphandler(timeout=timeout))
542 if has_https:
542 if has_https:
543 handlers.append(httpshandler(ui, timeout=timeout))
543 handlers.append(httpshandler(ui, timeout=timeout))
544
544
545 handlers.append(proxyhandler(ui))
545 handlers.append(proxyhandler(ui))
546
546
547 passmgr = passwordmgr(ui, ui.httppasswordmgrdb)
547 passmgr = passwordmgr(ui, ui.httppasswordmgrdb)
548 if authinfo is not None:
548 if authinfo is not None:
549 realm, uris, user, passwd = authinfo
549 realm, uris, user, passwd = authinfo
550 saveduser, savedpass = passmgr.find_stored_password(uris[0])
550 saveduser, savedpass = passmgr.find_stored_password(uris[0])
551 if user != saveduser or passwd:
551 if user != saveduser or passwd:
552 passmgr.add_password(realm, uris, user, passwd)
552 passmgr.add_password(realm, uris, user, passwd)
553 ui.debug('http auth: user %s, password %s\n' %
553 ui.debug('http auth: user %s, password %s\n' %
554 (user, passwd and '*' * len(passwd) or 'not set'))
554 (user, passwd and '*' * len(passwd) or 'not set'))
555
555
556 handlers.extend((httpbasicauthhandler(passmgr),
556 handlers.extend((httpbasicauthhandler(passmgr),
557 httpdigestauthhandler(passmgr)))
557 httpdigestauthhandler(passmgr)))
558 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
558 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
559 handlers.append(cookiehandler(ui))
559 handlers.append(cookiehandler(ui))
560 opener = urlreq.buildopener(*handlers)
560 opener = urlreq.buildopener(*handlers)
561
561
562 # keepalive.py's handlers will populate these attributes if they exist.
562 # keepalive.py's handlers will populate these attributes if they exist.
563 opener.requestscount = 0
563 opener.requestscount = 0
564 opener.sentbytescount = 0
564 opener.sentbytescount = 0
565 opener.receivedbytescount = 0
565 opener.receivedbytescount = 0
566
566
567 # The user agent should should *NOT* be used by servers for e.g.
567 # The user agent should should *NOT* be used by servers for e.g.
568 # protocol detection or feature negotiation: there are other
568 # protocol detection or feature negotiation: there are other
569 # facilities for that.
569 # facilities for that.
570 #
570 #
571 # "mercurial/proto-1.0" was the original user agent string and
571 # "mercurial/proto-1.0" was the original user agent string and
572 # exists for backwards compatibility reasons.
572 # exists for backwards compatibility reasons.
573 #
573 #
574 # The "(Mercurial %s)" string contains the distribution
574 # The "(Mercurial %s)" string contains the distribution
575 # name and version. Other client implementations should choose their
575 # name and version. Other client implementations should choose their
576 # own distribution name. Since servers should not be using the user
576 # own distribution name. Since servers should not be using the user
577 # agent string for anything, clients should be able to define whatever
577 # agent string for anything, clients should be able to define whatever
578 # user agent they deem appropriate.
578 # user agent they deem appropriate.
579 #
579 #
580 # The custom user agent is for lfs, because unfortunately some servers
580 # The custom user agent is for lfs, because unfortunately some servers
581 # do look at this value.
581 # do look at this value.
582 if not useragent:
582 if not useragent:
583 agent = 'mercurial/proto-1.0 (Mercurial %s)' % util.version()
583 agent = 'mercurial/proto-1.0 (Mercurial %s)' % util.version()
584 opener.addheaders = [(r'User-agent', pycompat.sysstr(agent))]
584 opener.addheaders = [(r'User-agent', pycompat.sysstr(agent))]
585 else:
585 else:
586 opener.addheaders = [(r'User-agent', pycompat.sysstr(useragent))]
586 opener.addheaders = [(r'User-agent', pycompat.sysstr(useragent))]
587
587
588 # This header should only be needed by wire protocol requests. But it has
588 # This header should only be needed by wire protocol requests. But it has
589 # been sent on all requests since forever. We keep sending it for backwards
589 # been sent on all requests since forever. We keep sending it for backwards
590 # compatibility reasons. Modern versions of the wire protocol use
590 # compatibility reasons. Modern versions of the wire protocol use
591 # X-HgProto-<N> for advertising client support.
591 # X-HgProto-<N> for advertising client support.
592 if sendaccept:
592 if sendaccept:
593 opener.addheaders.append((r'Accept', r'application/mercurial-0.1'))
593 opener.addheaders.append((r'Accept', r'application/mercurial-0.1'))
594
594
595 return opener
595 return opener
596
596
597 def open(ui, url_, data=None):
597 def open(ui, url_, data=None):
598 u = util.url(url_)
598 u = util.url(url_)
599 if u.scheme:
599 if u.scheme:
600 u.scheme = u.scheme.lower()
600 u.scheme = u.scheme.lower()
601 url_, authinfo = u.authinfo()
601 url_, authinfo = u.authinfo()
602 else:
602 else:
603 path = util.normpath(os.path.abspath(url_))
603 path = util.normpath(os.path.abspath(url_))
604 url_ = 'file://' + pycompat.bytesurl(urlreq.pathname2url(path))
604 url_ = 'file://' + pycompat.bytesurl(urlreq.pathname2url(path))
605 authinfo = None
605 authinfo = None
606 return opener(ui, authinfo).open(pycompat.strurl(url_), data)
606 return opener(ui, authinfo).open(pycompat.strurl(url_), data)
607
607
608 def wrapresponse(resp):
608 def wrapresponse(resp):
609 """Wrap a response object with common error handlers.
609 """Wrap a response object with common error handlers.
610
610
611 This ensures that any I/O from any consumer raises the appropriate
611 This ensures that any I/O from any consumer raises the appropriate
612 error and messaging.
612 error and messaging.
613 """
613 """
614 origread = resp.read
614 origread = resp.read
615
615
616 class readerproxy(resp.__class__):
616 class readerproxy(resp.__class__):
617 def read(self, size=None):
617 def read(self, size=None):
618 try:
618 try:
619 return origread(size)
619 return origread(size)
620 except httplib.IncompleteRead as e:
620 except httplib.IncompleteRead as e:
621 # e.expected is an integer if length known or None otherwise.
621 # e.expected is an integer if length known or None otherwise.
622 if e.expected:
622 if e.expected:
623 got = len(e.partial)
623 got = len(e.partial)
624 total = e.expected + got
624 total = e.expected + got
625 msg = _('HTTP request error (incomplete response; '
625 msg = _('HTTP request error (incomplete response; '
626 'expected %d bytes got %d)') % (total, got)
626 'expected %d bytes got %d)') % (total, got)
627 else:
627 else:
628 msg = _('HTTP request error (incomplete response)')
628 msg = _('HTTP request error (incomplete response)')
629
629
630 raise error.PeerTransportError(
630 raise error.PeerTransportError(
631 msg,
631 msg,
632 hint=_('this may be an intermittent network failure; '
632 hint=_('this may be an intermittent network failure; '
633 'if the error persists, consider contacting the '
633 'if the error persists, consider contacting the '
634 'network or server operator'))
634 'network or server operator'))
635 except httplib.HTTPException as e:
635 except httplib.HTTPException as e:
636 raise error.PeerTransportError(
636 raise error.PeerTransportError(
637 _('HTTP request error (%s)') % e,
637 _('HTTP request error (%s)') % e,
638 hint=_('this may be an intermittent network failure; '
638 hint=_('this may be an intermittent network failure; '
639 'if the error persists, consider contacting the '
639 'if the error persists, consider contacting the '
640 'network or server operator'))
640 'network or server operator'))
641
641
642 resp.__class__ = readerproxy
642 resp.__class__ = readerproxy
@@ -1,584 +1,581 b''
1 #require serve
1 #require serve
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo foo>foo
5 $ echo foo>foo
6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
7 $ echo foo>foo.d/foo
7 $ echo foo>foo.d/foo
8 $ echo bar>foo.d/bAr.hg.d/BaR
8 $ echo bar>foo.d/bAr.hg.d/BaR
9 $ echo bar>foo.d/baR.d.hg/bAR
9 $ echo bar>foo.d/baR.d.hg/bAR
10 $ hg commit -A -m 1
10 $ hg commit -A -m 1
11 adding foo
11 adding foo
12 adding foo.d/bAr.hg.d/BaR
12 adding foo.d/bAr.hg.d/BaR
13 adding foo.d/baR.d.hg/bAR
13 adding foo.d/baR.d.hg/bAR
14 adding foo.d/foo
14 adding foo.d/foo
15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
16 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
16 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
17
17
18 Test server address cannot be reused
18 Test server address cannot be reused
19
19
20 $ hg serve -p $HGPORT1 2>&1
20 $ hg serve -p $HGPORT1 2>&1
21 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
21 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
22 [255]
22 [255]
23
23
24 $ cd ..
24 $ cd ..
25 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
25 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
26
26
27 clone via stream
27 clone via stream
28
28
29 #if no-reposimplestore
29 #if no-reposimplestore
30 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
30 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
31 streaming all changes
31 streaming all changes
32 9 files to transfer, 715 bytes of data
32 9 files to transfer, 715 bytes of data
33 transferred * bytes in * seconds (*/sec) (glob)
33 transferred * bytes in * seconds (*/sec) (glob)
34 updating to branch default
34 updating to branch default
35 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 $ hg verify -R copy
36 $ hg verify -R copy
37 checking changesets
37 checking changesets
38 checking manifests
38 checking manifests
39 crosschecking files in changesets and manifests
39 crosschecking files in changesets and manifests
40 checking files
40 checking files
41 checked 1 changesets with 4 changes to 4 files
41 checked 1 changesets with 4 changes to 4 files
42 #endif
42 #endif
43
43
44 try to clone via stream, should use pull instead
44 try to clone via stream, should use pull instead
45
45
46 $ hg clone --stream http://localhost:$HGPORT1/ copy2
46 $ hg clone --stream http://localhost:$HGPORT1/ copy2
47 warning: stream clone requested but server has them disabled
47 warning: stream clone requested but server has them disabled
48 requesting all changes
48 requesting all changes
49 adding changesets
49 adding changesets
50 adding manifests
50 adding manifests
51 adding file changes
51 adding file changes
52 added 1 changesets with 4 changes to 4 files
52 added 1 changesets with 4 changes to 4 files
53 new changesets 8b6053c928fe
53 new changesets 8b6053c928fe
54 updating to branch default
54 updating to branch default
55 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
56
56
57 try to clone via stream but missing requirements, so should use pull instead
57 try to clone via stream but missing requirements, so should use pull instead
58
58
59 $ cat > $TESTTMP/removesupportedformat.py << EOF
59 $ cat > $TESTTMP/removesupportedformat.py << EOF
60 > from mercurial import localrepo
60 > from mercurial import localrepo
61 > def extsetup(ui):
61 > def extsetup(ui):
62 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
62 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
63 > EOF
63 > EOF
64
64
65 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
65 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
66 warning: stream clone requested but client is missing requirements: generaldelta
66 warning: stream clone requested but client is missing requirements: generaldelta
67 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
67 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
68 requesting all changes
68 requesting all changes
69 adding changesets
69 adding changesets
70 adding manifests
70 adding manifests
71 adding file changes
71 adding file changes
72 added 1 changesets with 4 changes to 4 files
72 added 1 changesets with 4 changes to 4 files
73 new changesets 8b6053c928fe
73 new changesets 8b6053c928fe
74 updating to branch default
74 updating to branch default
75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
76
76
77 clone via pull
77 clone via pull
78
78
79 $ hg clone http://localhost:$HGPORT1/ copy-pull
79 $ hg clone http://localhost:$HGPORT1/ copy-pull
80 requesting all changes
80 requesting all changes
81 adding changesets
81 adding changesets
82 adding manifests
82 adding manifests
83 adding file changes
83 adding file changes
84 added 1 changesets with 4 changes to 4 files
84 added 1 changesets with 4 changes to 4 files
85 new changesets 8b6053c928fe
85 new changesets 8b6053c928fe
86 updating to branch default
86 updating to branch default
87 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 $ hg verify -R copy-pull
88 $ hg verify -R copy-pull
89 checking changesets
89 checking changesets
90 checking manifests
90 checking manifests
91 crosschecking files in changesets and manifests
91 crosschecking files in changesets and manifests
92 checking files
92 checking files
93 checked 1 changesets with 4 changes to 4 files
93 checked 1 changesets with 4 changes to 4 files
94 $ cd test
94 $ cd test
95 $ echo bar > bar
95 $ echo bar > bar
96 $ hg commit -A -d '1 0' -m 2
96 $ hg commit -A -d '1 0' -m 2
97 adding bar
97 adding bar
98 $ cd ..
98 $ cd ..
99
99
100 clone over http with --update
100 clone over http with --update
101
101
102 $ hg clone http://localhost:$HGPORT1/ updated --update 0
102 $ hg clone http://localhost:$HGPORT1/ updated --update 0
103 requesting all changes
103 requesting all changes
104 adding changesets
104 adding changesets
105 adding manifests
105 adding manifests
106 adding file changes
106 adding file changes
107 added 2 changesets with 5 changes to 5 files
107 added 2 changesets with 5 changes to 5 files
108 new changesets 8b6053c928fe:5fed3813f7f5
108 new changesets 8b6053c928fe:5fed3813f7f5
109 updating to branch default
109 updating to branch default
110 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 $ hg log -r . -R updated
111 $ hg log -r . -R updated
112 changeset: 0:8b6053c928fe
112 changeset: 0:8b6053c928fe
113 user: test
113 user: test
114 date: Thu Jan 01 00:00:00 1970 +0000
114 date: Thu Jan 01 00:00:00 1970 +0000
115 summary: 1
115 summary: 1
116
116
117 $ rm -rf updated
117 $ rm -rf updated
118
118
119 incoming via HTTP
119 incoming via HTTP
120
120
121 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
121 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
122 adding changesets
122 adding changesets
123 adding manifests
123 adding manifests
124 adding file changes
124 adding file changes
125 added 1 changesets with 4 changes to 4 files
125 added 1 changesets with 4 changes to 4 files
126 new changesets 8b6053c928fe
126 new changesets 8b6053c928fe
127 updating to branch default
127 updating to branch default
128 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 $ cd partial
129 $ cd partial
130 $ touch LOCAL
130 $ touch LOCAL
131 $ hg ci -qAm LOCAL
131 $ hg ci -qAm LOCAL
132 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
132 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
133 comparing with http://localhost:$HGPORT1/
133 comparing with http://localhost:$HGPORT1/
134 searching for changes
134 searching for changes
135 2
135 2
136 $ cd ..
136 $ cd ..
137
137
138 pull
138 pull
139
139
140 $ cd copy-pull
140 $ cd copy-pull
141 $ cat >> .hg/hgrc <<EOF
141 $ cat >> .hg/hgrc <<EOF
142 > [hooks]
142 > [hooks]
143 > changegroup = sh -c "printenv.py --line changegroup"
143 > changegroup = sh -c "printenv.py --line changegroup"
144 > EOF
144 > EOF
145 $ hg pull
145 $ hg pull
146 pulling from http://localhost:$HGPORT1/
146 pulling from http://localhost:$HGPORT1/
147 searching for changes
147 searching for changes
148 adding changesets
148 adding changesets
149 adding manifests
149 adding manifests
150 adding file changes
150 adding file changes
151 added 1 changesets with 1 changes to 1 files
151 added 1 changesets with 1 changes to 1 files
152 new changesets 5fed3813f7f5
152 new changesets 5fed3813f7f5
153 changegroup hook: HG_HOOKNAME=changegroup
153 changegroup hook: HG_HOOKNAME=changegroup
154 HG_HOOKTYPE=changegroup
154 HG_HOOKTYPE=changegroup
155 HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
155 HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
156 HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
156 HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
157 HG_SOURCE=pull
157 HG_SOURCE=pull
158 HG_TXNID=TXN:$ID$
158 HG_TXNID=TXN:$ID$
159 HG_URL=http://localhost:$HGPORT1/
159 HG_URL=http://localhost:$HGPORT1/
160
160
161 (run 'hg update' to get a working copy)
161 (run 'hg update' to get a working copy)
162 $ cd ..
162 $ cd ..
163
163
164 clone from invalid URL
164 clone from invalid URL
165
165
166 $ hg clone http://localhost:$HGPORT/bad
166 $ hg clone http://localhost:$HGPORT/bad
167 abort: HTTP Error 404: Not Found
167 abort: HTTP Error 404: Not Found
168 [255]
168 [255]
169
169
170 test http authentication
170 test http authentication
171 + use the same server to test server side streaming preference
171 + use the same server to test server side streaming preference
172
172
173 $ cd test
173 $ cd test
174
174
175 $ hg serve --config extensions.x=$TESTDIR/httpserverauth.py -p $HGPORT2 -d \
175 $ hg serve --config extensions.x=$TESTDIR/httpserverauth.py -p $HGPORT2 -d \
176 > --pid-file=pid --config server.preferuncompressed=True -E ../errors2.log \
176 > --pid-file=pid --config server.preferuncompressed=True -E ../errors2.log \
177 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
177 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
178 $ cat pid >> $DAEMON_PIDS
178 $ cat pid >> $DAEMON_PIDS
179
179
180 $ cat << EOF > get_pass.py
180 $ cat << EOF > get_pass.py
181 > import getpass
181 > import getpass
182 > def newgetpass(arg):
182 > def newgetpass(arg):
183 > return "pass"
183 > return "pass"
184 > getpass.getpass = newgetpass
184 > getpass.getpass = newgetpass
185 > EOF
185 > EOF
186
186
187 $ hg id http://localhost:$HGPORT2/
187 $ hg id http://localhost:$HGPORT2/
188 abort: http authorization required for http://localhost:$HGPORT2/
188 abort: http authorization required for http://localhost:$HGPORT2/
189 [255]
189 [255]
190 $ hg id http://localhost:$HGPORT2/
190 $ hg id http://localhost:$HGPORT2/
191 abort: http authorization required for http://localhost:$HGPORT2/
191 abort: http authorization required for http://localhost:$HGPORT2/
192 [255]
192 [255]
193 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
193 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
194 http authorization required for http://localhost:$HGPORT2/
194 http authorization required for http://localhost:$HGPORT2/
195 realm: mercurial
195 realm: mercurial
196 user: user
196 user: user
197 password: 5fed3813f7f5
197 password: 5fed3813f7f5
198 $ hg id http://user:pass@localhost:$HGPORT2/
198 $ hg id http://user:pass@localhost:$HGPORT2/
199 5fed3813f7f5
199 5fed3813f7f5
200 $ echo '[auth]' >> .hg/hgrc
200 $ echo '[auth]' >> .hg/hgrc
201 $ echo 'l.schemes=http' >> .hg/hgrc
201 $ echo 'l.schemes=http' >> .hg/hgrc
202 $ echo 'l.prefix=lo' >> .hg/hgrc
202 $ echo 'l.prefix=lo' >> .hg/hgrc
203 $ echo 'l.username=user' >> .hg/hgrc
203 $ echo 'l.username=user' >> .hg/hgrc
204 $ echo 'l.password=pass' >> .hg/hgrc
204 $ echo 'l.password=pass' >> .hg/hgrc
205 $ hg id http://localhost:$HGPORT2/
205 $ hg id http://localhost:$HGPORT2/
206 5fed3813f7f5
206 5fed3813f7f5
207 $ hg id http://localhost:$HGPORT2/
207 $ hg id http://localhost:$HGPORT2/
208 5fed3813f7f5
208 5fed3813f7f5
209 $ hg id http://user@localhost:$HGPORT2/
209 $ hg id http://user@localhost:$HGPORT2/
210 5fed3813f7f5
210 5fed3813f7f5
211
211
212 $ cat > use_digests.py << EOF
212 $ cat > use_digests.py << EOF
213 > from mercurial import (
213 > from mercurial import (
214 > exthelper,
214 > exthelper,
215 > url,
215 > url,
216 > )
216 > )
217 >
217 >
218 > eh = exthelper.exthelper()
218 > eh = exthelper.exthelper()
219 > uisetup = eh.finaluisetup
219 > uisetup = eh.finaluisetup
220 >
220 >
221 > @eh.wrapfunction(url, 'opener')
221 > @eh.wrapfunction(url, 'opener')
222 > def urlopener(orig, *args, **kwargs):
222 > def urlopener(orig, *args, **kwargs):
223 > opener = orig(*args, **kwargs)
223 > opener = orig(*args, **kwargs)
224 > opener.addheaders.append((r'X-HgTest-AuthType', r'Digest'))
224 > opener.addheaders.append((r'X-HgTest-AuthType', r'Digest'))
225 > return opener
225 > return opener
226 > EOF
226 > EOF
227
227
228 $ hg id http://localhost:$HGPORT2/ --config extensions.x=use_digests.py || true
228 $ hg id http://localhost:$HGPORT2/ --config extensions.x=use_digests.py
229 abort: HTTP Error 403: bad user (py3 !)
229 5fed3813f7f5
230 5fed3813f7f5 (no-py3 !)
231
230
232 #if no-reposimplestore
231 #if no-reposimplestore
233 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
232 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
234 streaming all changes
233 streaming all changes
235 10 files to transfer, 1.01 KB of data
234 10 files to transfer, 1.01 KB of data
236 transferred * KB in * seconds (*/sec) (glob)
235 transferred * KB in * seconds (*/sec) (glob)
237 updating to branch default
236 updating to branch default
238 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 #endif
238 #endif
240
239
241 --pull should override server's preferuncompressed
240 --pull should override server's preferuncompressed
242 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
241 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
243 requesting all changes
242 requesting all changes
244 adding changesets
243 adding changesets
245 adding manifests
244 adding manifests
246 adding file changes
245 adding file changes
247 added 2 changesets with 5 changes to 5 files
246 added 2 changesets with 5 changes to 5 files
248 new changesets 8b6053c928fe:5fed3813f7f5
247 new changesets 8b6053c928fe:5fed3813f7f5
249 updating to branch default
248 updating to branch default
250 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
251
250
252 $ hg id http://user2@localhost:$HGPORT2/
251 $ hg id http://user2@localhost:$HGPORT2/
253 abort: http authorization required for http://localhost:$HGPORT2/
252 abort: http authorization required for http://localhost:$HGPORT2/
254 [255]
253 [255]
255 $ hg id http://user:pass2@localhost:$HGPORT2/
254 $ hg id http://user:pass2@localhost:$HGPORT2/
256 abort: HTTP Error 403: no
255 abort: HTTP Error 403: no
257 [255]
256 [255]
258
257
259 $ hg -R dest-pull tag -r tip top
258 $ hg -R dest-pull tag -r tip top
260 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
259 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
261 pushing to http://user:***@localhost:$HGPORT2/
260 pushing to http://user:***@localhost:$HGPORT2/
262 searching for changes
261 searching for changes
263 remote: adding changesets
262 remote: adding changesets
264 remote: adding manifests
263 remote: adding manifests
265 remote: adding file changes
264 remote: adding file changes
266 remote: added 1 changesets with 1 changes to 1 files
265 remote: added 1 changesets with 1 changes to 1 files
267 $ hg rollback -q
266 $ hg rollback -q
268 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/ --debug --config devel.debug.peer-request=yes
267 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/ --debug --config devel.debug.peer-request=yes
269 pushing to http://user:***@localhost:$HGPORT2/
268 pushing to http://user:***@localhost:$HGPORT2/
270 using http://localhost:$HGPORT2/
269 using http://localhost:$HGPORT2/
271 http auth: user user, password ****
270 http auth: user user, password ****
272 sending capabilities command
271 sending capabilities command
273 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=capabilities
272 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=capabilities
274 http auth: user user, password ****
273 http auth: user user, password ****
275 devel-peer-request: finished in *.???? seconds (200) (glob)
274 devel-peer-request: finished in *.???? seconds (200) (glob)
276 query 1; heads
275 query 1; heads
277 devel-peer-request: batched-content
276 devel-peer-request: batched-content
278 devel-peer-request: - heads (0 arguments)
277 devel-peer-request: - heads (0 arguments)
279 devel-peer-request: - known (1 arguments)
278 devel-peer-request: - known (1 arguments)
280 sending batch command
279 sending batch command
281 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=batch
280 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=batch
282 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
281 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
283 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
282 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
284 devel-peer-request: 68 bytes of commands arguments in headers
283 devel-peer-request: 68 bytes of commands arguments in headers
285 devel-peer-request: finished in *.???? seconds (200) (glob)
284 devel-peer-request: finished in *.???? seconds (200) (glob)
286 searching for changes
285 searching for changes
287 all remote heads known locally
286 all remote heads known locally
288 preparing listkeys for "phases"
287 preparing listkeys for "phases"
289 sending listkeys command
288 sending listkeys command
290 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
289 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
291 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
290 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
292 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
291 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
293 devel-peer-request: 16 bytes of commands arguments in headers
292 devel-peer-request: 16 bytes of commands arguments in headers
294 devel-peer-request: finished in *.???? seconds (200) (glob)
293 devel-peer-request: finished in *.???? seconds (200) (glob)
295 received listkey for "phases": 58 bytes
294 received listkey for "phases": 58 bytes
296 checking for updated bookmarks
295 checking for updated bookmarks
297 preparing listkeys for "bookmarks"
296 preparing listkeys for "bookmarks"
298 sending listkeys command
297 sending listkeys command
299 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
298 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
300 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
299 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
301 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
300 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
302 devel-peer-request: 19 bytes of commands arguments in headers
301 devel-peer-request: 19 bytes of commands arguments in headers
303 devel-peer-request: finished in *.???? seconds (200) (glob)
302 devel-peer-request: finished in *.???? seconds (200) (glob)
304 received listkey for "bookmarks": 0 bytes
303 received listkey for "bookmarks": 0 bytes
305 sending branchmap command
304 sending branchmap command
306 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
305 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
307 devel-peer-request: Vary X-HgProto-1
306 devel-peer-request: Vary X-HgProto-1
308 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
307 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
309 devel-peer-request: finished in *.???? seconds (200) (glob)
308 devel-peer-request: finished in *.???? seconds (200) (glob)
310 preparing listkeys for "bookmarks"
309 preparing listkeys for "bookmarks"
311 sending listkeys command
310 sending listkeys command
312 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
311 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
313 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
312 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
314 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
313 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
315 devel-peer-request: 19 bytes of commands arguments in headers
314 devel-peer-request: 19 bytes of commands arguments in headers
316 devel-peer-request: finished in *.???? seconds (200) (glob)
315 devel-peer-request: finished in *.???? seconds (200) (glob)
317 received listkey for "bookmarks": 0 bytes
316 received listkey for "bookmarks": 0 bytes
318 1 changesets found
317 1 changesets found
319 list of changesets:
318 list of changesets:
320 7f4e523d01f2cc3765ac8934da3d14db775ff872
319 7f4e523d01f2cc3765ac8934da3d14db775ff872
321 bundle2-output-bundle: "HG20", 5 parts total
320 bundle2-output-bundle: "HG20", 5 parts total
322 bundle2-output-part: "replycaps" 205 bytes payload
321 bundle2-output-part: "replycaps" 205 bytes payload
323 bundle2-output-part: "check:phases" 24 bytes payload
322 bundle2-output-part: "check:phases" 24 bytes payload
324 bundle2-output-part: "check:heads" streamed payload
323 bundle2-output-part: "check:heads" streamed payload
325 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
324 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
326 bundle2-output-part: "phase-heads" 24 bytes payload
325 bundle2-output-part: "phase-heads" 24 bytes payload
327 sending unbundle command
326 sending unbundle command
328 sending 1013 bytes
327 sending 1013 bytes
329 devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
328 devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
330 devel-peer-request: Content-length 1013
329 devel-peer-request: Content-length 1013
331 devel-peer-request: Content-type application/mercurial-0.1
330 devel-peer-request: Content-type application/mercurial-0.1
332 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
331 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
333 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
332 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
334 devel-peer-request: 16 bytes of commands arguments in headers
333 devel-peer-request: 16 bytes of commands arguments in headers
335 devel-peer-request: 1013 bytes of data
334 devel-peer-request: 1013 bytes of data
336 devel-peer-request: finished in *.???? seconds (200) (glob)
335 devel-peer-request: finished in *.???? seconds (200) (glob)
337 bundle2-input-bundle: no-transaction
336 bundle2-input-bundle: no-transaction
338 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
337 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
339 bundle2-input-part: "output" (advisory) (params: 0 advisory) supported
338 bundle2-input-part: "output" (advisory) (params: 0 advisory) supported
340 bundle2-input-part: total payload size 100
339 bundle2-input-part: total payload size 100
341 remote: adding changesets
340 remote: adding changesets
342 remote: adding manifests
341 remote: adding manifests
343 remote: adding file changes
342 remote: adding file changes
344 remote: added 1 changesets with 1 changes to 1 files
343 remote: added 1 changesets with 1 changes to 1 files
345 bundle2-input-part: "output" (advisory) supported
344 bundle2-input-part: "output" (advisory) supported
346 bundle2-input-bundle: 2 parts total
345 bundle2-input-bundle: 2 parts total
347 preparing listkeys for "phases"
346 preparing listkeys for "phases"
348 sending listkeys command
347 sending listkeys command
349 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
348 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
350 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
349 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
351 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
350 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
352 devel-peer-request: 16 bytes of commands arguments in headers
351 devel-peer-request: 16 bytes of commands arguments in headers
353 devel-peer-request: finished in *.???? seconds (200) (glob)
352 devel-peer-request: finished in *.???? seconds (200) (glob)
354 received listkey for "phases": 15 bytes
353 received listkey for "phases": 15 bytes
355 $ hg rollback -q
354 $ hg rollback -q
356
355
357 $ sed 's/.*] "/"/' < ../access.log
356 $ sed 's/.*] "/"/' < ../access.log
358 "GET /?cmd=capabilities HTTP/1.1" 401 -
357 "GET /?cmd=capabilities HTTP/1.1" 401 -
359 "GET /?cmd=capabilities HTTP/1.1" 401 -
358 "GET /?cmd=capabilities HTTP/1.1" 401 -
360 "GET /?cmd=capabilities HTTP/1.1" 401 -
359 "GET /?cmd=capabilities HTTP/1.1" 401 -
361 "GET /?cmd=capabilities HTTP/1.1" 200 -
360 "GET /?cmd=capabilities HTTP/1.1" 200 -
362 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
361 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
363 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
362 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
364 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
363 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
365 "GET /?cmd=capabilities HTTP/1.1" 401 -
364 "GET /?cmd=capabilities HTTP/1.1" 401 -
366 "GET /?cmd=capabilities HTTP/1.1" 200 -
365 "GET /?cmd=capabilities HTTP/1.1" 200 -
367 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
366 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
368 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
367 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
369 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
368 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
370 "GET /?cmd=capabilities HTTP/1.1" 401 -
369 "GET /?cmd=capabilities HTTP/1.1" 401 -
371 "GET /?cmd=capabilities HTTP/1.1" 200 -
370 "GET /?cmd=capabilities HTTP/1.1" 200 -
372 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
371 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
373 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
372 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
374 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
373 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
375 "GET /?cmd=capabilities HTTP/1.1" 401 -
374 "GET /?cmd=capabilities HTTP/1.1" 401 -
376 "GET /?cmd=capabilities HTTP/1.1" 200 -
375 "GET /?cmd=capabilities HTTP/1.1" 200 -
377 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
376 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
378 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
377 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
379 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
378 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
380 "GET /?cmd=capabilities HTTP/1.1" 401 -
379 "GET /?cmd=capabilities HTTP/1.1" 401 -
381 "GET /?cmd=capabilities HTTP/1.1" 200 -
380 "GET /?cmd=capabilities HTTP/1.1" 200 -
382 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
381 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
383 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
382 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
384 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
383 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
385 "GET /?cmd=capabilities HTTP/1.1" 401 - x-hgtest-authtype:Digest
384 "GET /?cmd=capabilities HTTP/1.1" 401 - x-hgtest-authtype:Digest
386 "GET /?cmd=capabilities HTTP/1.1" 403 - x-hgtest-authtype:Digest (py3 !)
385 "GET /?cmd=capabilities HTTP/1.1" 200 - x-hgtest-authtype:Digest
387 "GET /?cmd=capabilities HTTP/1.1" 200 - x-hgtest-authtype:Digest (no-py3 !)
386 "GET /?cmd=lookup HTTP/1.1" 401 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
388 "GET /?cmd=lookup HTTP/1.1" 401 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest (no-py3 !)
387 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
389 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest (no-py3 !)
388 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
390 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest (no-py3 !)
389 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
391 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest (no-py3 !)
390 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
392 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest (no-py3 !)
391 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
393 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest (no-py3 !)
394 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
392 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
395 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
393 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
396 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
394 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
397 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=0&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&stream=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
395 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=0&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&stream=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
398 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
396 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
399 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
397 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
400 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
398 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
401 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
399 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
402 "GET /?cmd=capabilities HTTP/1.1" 401 -
400 "GET /?cmd=capabilities HTTP/1.1" 401 -
403 "GET /?cmd=capabilities HTTP/1.1" 401 -
401 "GET /?cmd=capabilities HTTP/1.1" 401 -
404 "GET /?cmd=capabilities HTTP/1.1" 403 -
402 "GET /?cmd=capabilities HTTP/1.1" 403 -
405 "GET /?cmd=capabilities HTTP/1.1" 401 -
403 "GET /?cmd=capabilities HTTP/1.1" 401 -
406 "GET /?cmd=capabilities HTTP/1.1" 200 -
404 "GET /?cmd=capabilities HTTP/1.1" 200 -
407 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
405 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
408 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
406 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
409 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
407 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
410 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
408 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
411 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
409 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
412 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob)
410 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob)
413 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
411 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
414 "GET /?cmd=capabilities HTTP/1.1" 401 -
412 "GET /?cmd=capabilities HTTP/1.1" 401 -
415 "GET /?cmd=capabilities HTTP/1.1" 200 -
413 "GET /?cmd=capabilities HTTP/1.1" 200 -
416 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
414 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
417 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
415 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
418 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
416 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
419 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
417 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
420 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
418 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
421 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
419 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
422 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
420 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
423
421
424 $ cd ..
422 $ cd ..
425
423
426 clone of serve with repo in root and unserved subrepo (issue2970)
424 clone of serve with repo in root and unserved subrepo (issue2970)
427
425
428 $ hg --cwd test init sub
426 $ hg --cwd test init sub
429 $ echo empty > test/sub/empty
427 $ echo empty > test/sub/empty
430 $ hg --cwd test/sub add empty
428 $ hg --cwd test/sub add empty
431 $ hg --cwd test/sub commit -qm 'add empty'
429 $ hg --cwd test/sub commit -qm 'add empty'
432 $ hg --cwd test/sub tag -r 0 something
430 $ hg --cwd test/sub tag -r 0 something
433 $ echo sub = sub > test/.hgsub
431 $ echo sub = sub > test/.hgsub
434 $ hg --cwd test add .hgsub
432 $ hg --cwd test add .hgsub
435 $ hg --cwd test commit -qm 'add subrepo'
433 $ hg --cwd test commit -qm 'add subrepo'
436 $ hg clone http://localhost:$HGPORT noslash-clone
434 $ hg clone http://localhost:$HGPORT noslash-clone
437 requesting all changes
435 requesting all changes
438 adding changesets
436 adding changesets
439 adding manifests
437 adding manifests
440 adding file changes
438 adding file changes
441 added 3 changesets with 7 changes to 7 files
439 added 3 changesets with 7 changes to 7 files
442 new changesets 8b6053c928fe:56f9bc90cce6
440 new changesets 8b6053c928fe:56f9bc90cce6
443 updating to branch default
441 updating to branch default
444 cloning subrepo sub from http://localhost:$HGPORT/sub
442 cloning subrepo sub from http://localhost:$HGPORT/sub
445 abort: HTTP Error 404: Not Found
443 abort: HTTP Error 404: Not Found
446 [255]
444 [255]
447 $ hg clone http://localhost:$HGPORT/ slash-clone
445 $ hg clone http://localhost:$HGPORT/ slash-clone
448 requesting all changes
446 requesting all changes
449 adding changesets
447 adding changesets
450 adding manifests
448 adding manifests
451 adding file changes
449 adding file changes
452 added 3 changesets with 7 changes to 7 files
450 added 3 changesets with 7 changes to 7 files
453 new changesets 8b6053c928fe:56f9bc90cce6
451 new changesets 8b6053c928fe:56f9bc90cce6
454 updating to branch default
452 updating to branch default
455 cloning subrepo sub from http://localhost:$HGPORT/sub
453 cloning subrepo sub from http://localhost:$HGPORT/sub
456 abort: HTTP Error 404: Not Found
454 abort: HTTP Error 404: Not Found
457 [255]
455 [255]
458
456
459 check error log
457 check error log
460
458
461 $ cat error.log
459 $ cat error.log
462
460
463 $ cat errors2.log
461 $ cat errors2.log
464 $LOCALIP - - [$ERRDATE$] HG error: No hash found for user/realm "b'user'/mercurial" (glob) (py3 !)
465
462
466 check abort error reporting while pulling/cloning
463 check abort error reporting while pulling/cloning
467
464
468 $ $RUNTESTDIR/killdaemons.py
465 $ $RUNTESTDIR/killdaemons.py
469 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
466 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
470 $ cat hg3.pid >> $DAEMON_PIDS
467 $ cat hg3.pid >> $DAEMON_PIDS
471 $ hg clone http://localhost:$HGPORT/ abort-clone
468 $ hg clone http://localhost:$HGPORT/ abort-clone
472 requesting all changes
469 requesting all changes
473 remote: abort: this is an exercise
470 remote: abort: this is an exercise
474 abort: pull failed on remote
471 abort: pull failed on remote
475 [255]
472 [255]
476 $ cat error.log
473 $ cat error.log
477
474
478 disable pull-based clones
475 disable pull-based clones
479
476
480 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
477 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
481 $ cat hg4.pid >> $DAEMON_PIDS
478 $ cat hg4.pid >> $DAEMON_PIDS
482 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
479 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
483 requesting all changes
480 requesting all changes
484 remote: abort: server has pull-based clones disabled
481 remote: abort: server has pull-based clones disabled
485 abort: pull failed on remote
482 abort: pull failed on remote
486 (remove --pull if specified or upgrade Mercurial)
483 (remove --pull if specified or upgrade Mercurial)
487 [255]
484 [255]
488
485
489 #if no-reposimplestore
486 #if no-reposimplestore
490 ... but keep stream clones working
487 ... but keep stream clones working
491
488
492 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
489 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
493 streaming all changes
490 streaming all changes
494 * files to transfer, * of data (glob)
491 * files to transfer, * of data (glob)
495 transferred * in * seconds (*/sec) (glob)
492 transferred * in * seconds (*/sec) (glob)
496 $ cat error.log
493 $ cat error.log
497 #endif
494 #endif
498
495
499 ... and also keep partial clones and pulls working
496 ... and also keep partial clones and pulls working
500 $ hg clone http://localhost:$HGPORT1 --rev 0 test/partial/clone
497 $ hg clone http://localhost:$HGPORT1 --rev 0 test/partial/clone
501 adding changesets
498 adding changesets
502 adding manifests
499 adding manifests
503 adding file changes
500 adding file changes
504 added 1 changesets with 4 changes to 4 files
501 added 1 changesets with 4 changes to 4 files
505 new changesets 8b6053c928fe
502 new changesets 8b6053c928fe
506 updating to branch default
503 updating to branch default
507 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
504 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
508 $ hg pull -R test/partial/clone
505 $ hg pull -R test/partial/clone
509 pulling from http://localhost:$HGPORT1/
506 pulling from http://localhost:$HGPORT1/
510 searching for changes
507 searching for changes
511 adding changesets
508 adding changesets
512 adding manifests
509 adding manifests
513 adding file changes
510 adding file changes
514 added 2 changesets with 3 changes to 3 files
511 added 2 changesets with 3 changes to 3 files
515 new changesets 5fed3813f7f5:56f9bc90cce6
512 new changesets 5fed3813f7f5:56f9bc90cce6
516 (run 'hg update' to get a working copy)
513 (run 'hg update' to get a working copy)
517
514
518 $ hg clone -U -r 0 test/partial/clone test/another/clone
515 $ hg clone -U -r 0 test/partial/clone test/another/clone
519 adding changesets
516 adding changesets
520 adding manifests
517 adding manifests
521 adding file changes
518 adding file changes
522 added 1 changesets with 4 changes to 4 files
519 added 1 changesets with 4 changes to 4 files
523 new changesets 8b6053c928fe
520 new changesets 8b6053c928fe
524
521
525 corrupt cookies file should yield a warning
522 corrupt cookies file should yield a warning
526
523
527 $ cat > $TESTTMP/cookies.txt << EOF
524 $ cat > $TESTTMP/cookies.txt << EOF
528 > bad format
525 > bad format
529 > EOF
526 > EOF
530
527
531 $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
528 $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
532 (error loading cookie file $TESTTMP/cookies.txt: '*/cookies.txt' does not look like a Netscape format cookies file; continuing without cookies) (glob)
529 (error loading cookie file $TESTTMP/cookies.txt: '*/cookies.txt' does not look like a Netscape format cookies file; continuing without cookies) (glob)
533 56f9bc90cce6
530 56f9bc90cce6
534
531
535 $ killdaemons.py
532 $ killdaemons.py
536
533
537 Create dummy authentication handler that looks for cookies. It doesn't do anything
534 Create dummy authentication handler that looks for cookies. It doesn't do anything
538 useful. It just raises an HTTP 500 with details about the Cookie request header.
535 useful. It just raises an HTTP 500 with details about the Cookie request header.
539 We raise HTTP 500 because its message is printed in the abort message.
536 We raise HTTP 500 because its message is printed in the abort message.
540
537
541 $ cat > cookieauth.py << EOF
538 $ cat > cookieauth.py << EOF
542 > from mercurial import util
539 > from mercurial import util
543 > from mercurial.hgweb import common
540 > from mercurial.hgweb import common
544 > def perform_authentication(hgweb, req, op):
541 > def perform_authentication(hgweb, req, op):
545 > cookie = req.headers.get(b'Cookie')
542 > cookie = req.headers.get(b'Cookie')
546 > if not cookie:
543 > if not cookie:
547 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'no-cookie')
544 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'no-cookie')
548 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'Cookie: %s' % cookie)
545 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'Cookie: %s' % cookie)
549 > def extsetup(ui):
546 > def extsetup(ui):
550 > common.permhooks.insert(0, perform_authentication)
547 > common.permhooks.insert(0, perform_authentication)
551 > EOF
548 > EOF
552
549
553 $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
550 $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
554 $ cat pid > $DAEMON_PIDS
551 $ cat pid > $DAEMON_PIDS
555
552
556 Request without cookie sent should fail due to lack of cookie
553 Request without cookie sent should fail due to lack of cookie
557
554
558 $ hg id http://localhost:$HGPORT
555 $ hg id http://localhost:$HGPORT
559 abort: HTTP Error 500: no-cookie
556 abort: HTTP Error 500: no-cookie
560 [255]
557 [255]
561
558
562 Populate a cookies file
559 Populate a cookies file
563
560
564 $ cat > cookies.txt << EOF
561 $ cat > cookies.txt << EOF
565 > # HTTP Cookie File
562 > # HTTP Cookie File
566 > # Expiration is 2030-01-01 at midnight
563 > # Expiration is 2030-01-01 at midnight
567 > .example.com TRUE / FALSE 1893456000 hgkey examplevalue
564 > .example.com TRUE / FALSE 1893456000 hgkey examplevalue
568 > EOF
565 > EOF
569
566
570 Should not send a cookie for another domain
567 Should not send a cookie for another domain
571
568
572 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
569 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
573 abort: HTTP Error 500: no-cookie
570 abort: HTTP Error 500: no-cookie
574 [255]
571 [255]
575
572
576 Add a cookie entry for our test server and verify it is sent
573 Add a cookie entry for our test server and verify it is sent
577
574
578 $ cat >> cookies.txt << EOF
575 $ cat >> cookies.txt << EOF
579 > localhost.local FALSE / FALSE 1893456000 hgkey localhostvalue
576 > localhost.local FALSE / FALSE 1893456000 hgkey localhostvalue
580 > EOF
577 > EOF
581
578
582 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
579 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
583 abort: HTTP Error 500: Cookie: hgkey=localhostvalue
580 abort: HTTP Error 500: Cookie: hgkey=localhostvalue
584 [255]
581 [255]
General Comments 0
You need to be logged in to leave comments. Login now