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