##// END OF EJS Templates
typing: use the `Status` protocol wherever `scmutil.status` was being used...
typing: use the `Status` protocol wherever `scmutil.status` was being used This likely isn't everything, but these were all of the places the latter was referenced in the generated *.pyi files, plus a few cases that were inferred as `Any`, but found in a module that was being changed anyway. We should figure out some sort of consistency as far as naming these Protocol classes (stdlib ones tend to be CamelCase and imported directly). The current convention of `from xxx.interfaces import foo as ifoo` is a little clever, but a little annoying to type out. Also, this package is likely to grow beyond just Protocol classes, where treating the types as interfaces is wrong (e.g. a theoretical `NodeT` type to represent the binary form of a node, instead of treating that and the incompatible hex form as both bytes). But that's a project for another day.

File last commit:

r52756:f4733654 default
r53349:9d79ffee default
Show More
httppeer.py
673 lines | 21.1 KiB | text/x-python | PythonLexer
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 # httppeer.py - HTTP repository proxy classes for mercurial
#
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 # Copyright 2005, 2006 Olivia Mackall <olivia@selenic.com>
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Matt Harbison
typing: add `from __future__ import annotations` to most files...
r52756 from __future__ import annotations
Gregory Szorc
httppeer: use absolute_import
r25954
import errno
Augie Fackler
httppeer: add support for httppostargs when we're sending a file...
r33820 import io
Gregory Szorc
httppeer: use absolute_import
r25954 import os
import socket
Gregory Szorc
httppeer: advertise and support application/mercurial-0.2...
r30763 import struct
Gregory Szorc
httppeer: use absolute_import
r25954
Augie Fackler
cleanup: directly use concurrent.futures instead of via pycompat...
r49690 from concurrent import futures
Gregory Szorc
httppeer: use absolute_import
r25954 from .i18n import _
from . import (
Martin von Zweigbergk
bundle: move writebundle() from changegroup.py to bundle2.py (API)...
r28666 bundle2,
Gregory Szorc
httppeer: use absolute_import
r25954 error,
httpconnection,
Pulkit Goyal
py3: convert the mode argument of os.fdopen to unicodes (1 of 2)...
r30924 pycompat,
Gregory Szorc
httppeer: use absolute_import
r25954 statichttprepo,
Gregory Szorc
httppeer: alias url as urlmod...
r36977 url as urlmod,
Gregory Szorc
httppeer: use absolute_import
r25954 util,
Gregory Szorc
wireproto: move version 1 peer functionality to standalone module (API)...
r37632 wireprotov1peer,
Gregory Szorc
httppeer: use absolute_import
r25954 )
Raphaël Gomès
exchangev2: remove it...
r49357 from .utils import urlutil
Peter Arrenbrecht
peer: introduce real peer classes...
r17192
Pulkit Goyal
py3: conditionalize httplib import...
r29455 httplib = util.httplib
timeless
pycompat: switch to util.urlreq/util.urlerr for py3 compat
r28883 urlerr = util.urlerr
urlreq = util.urlreq
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
httppeer: extract code for HTTP header spanning...
r30759 def encodevalueinheaders(value, header, limit):
"""Encode a string value into multiple HTTP headers.
``value`` will be encoded into 1 or more HTTP headers with the names
``header-<N>`` where ``<N>`` is an integer starting at 1. Each header
name + value will be at most ``limit`` bytes long.
Augie Fackler
httppeer: always produce native str header keys and values...
r34733 Returns an iterable of 2-tuples consisting of header names and
values as native strings.
Gregory Szorc
httppeer: extract code for HTTP header spanning...
r30759 """
Augie Fackler
httppeer: always produce native str header keys and values...
r34733 # HTTP Headers are ASCII. Python 3 requires them to be unicodes,
# not bytes. This function always takes bytes in as arguments.
fmt = pycompat.strurl(header) + r'-%s'
# Note: it is *NOT* a bug that the last bit here is a bytestring
# and not a unicode: we're just getting the encoded length anyway,
# and using an r-string to make it portable between Python 2 and 3
# doesn't work because then the \r is a literal backslash-r
# instead of a carriage return.
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 valuelen = limit - len(fmt % '000') - len(b': \r\n')
Gregory Szorc
httppeer: extract code for HTTP header spanning...
r30759 result = []
n = 0
Manuel Jacob
py3: replace `pycompat.xrange` by `range`
r50179 for i in range(0, len(value), valuelen):
Gregory Szorc
httppeer: extract code for HTTP header spanning...
r30759 n += 1
Augie Fackler
formatting: blacken the codebase...
r43346 result.append((fmt % str(n), pycompat.strurl(value[i : i + valuelen])))
Gregory Szorc
httppeer: extract code for HTTP header spanning...
r30759
return result
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class _multifile:
Augie Fackler
httppeer: add support for httppostargs when we're sending a file...
r33820 def __init__(self, *fileobjs):
for f in fileobjs:
safehasattr: drop usage in favor of hasattr...
r51821 if not hasattr(f, 'length'):
Augie Fackler
httppeer: add support for httppostargs when we're sending a file...
r33820 raise ValueError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'_multifile only supports file objects that '
b'have a length but this one does not:',
Augie Fackler
formatting: blacken the codebase...
r43346 type(f),
f,
)
Augie Fackler
httppeer: add support for httppostargs when we're sending a file...
r33820 self._fileobjs = fileobjs
self._index = 0
@property
def length(self):
return sum(f.length for f in self._fileobjs)
def read(self, amt=None):
if amt <= 0:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b''.join(f.read() for f in self._fileobjs)
Augie Fackler
httppeer: add support for httppostargs when we're sending a file...
r33820 parts = []
while amt and self._index < len(self._fileobjs):
parts.append(self._fileobjs[self._index].read(amt))
got = len(parts[-1])
if got < amt:
self._index += 1
amt -= got
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b''.join(parts)
Augie Fackler
httppeer: add support for httppostargs when we're sending a file...
r33820
def seek(self, offset, whence=os.SEEK_SET):
if whence != os.SEEK_SET:
raise NotImplementedError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'_multifile does not support anything other'
b' than os.SEEK_SET for whence on seek()'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
httppeer: add support for httppostargs when we're sending a file...
r33820 if offset != 0:
raise NotImplementedError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'_multifile only supports seeking to start, but that '
b'could be fixed if you need it'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
httppeer: add support for httppostargs when we're sending a file...
r33820 for f in self._fileobjs:
f.seek(0)
self._index = 0
Augie Fackler
formatting: blacken the codebase...
r43346
def makev1commandrequest(
Manuel Jacob
hidden: add support for --remote-hidden to HTTP peer...
r51313 ui,
requestbuilder,
caps,
capablefn,
repobaseurl,
cmd,
args,
remotehidden=False,
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 """Make an HTTP request to run a command for a version 1 client.
``caps`` is a set of known server capabilities. The value may be
None if capabilities are not yet known.
``capablefn`` is a function to evaluate a capability.
``cmd``, ``args``, and ``data`` define the command, its arguments, and
raw data to pass to it.
"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if cmd == b'pushkey':
args[b'data'] = b''
data = args.pop(b'data', None)
headers = args.pop(b'headers', {})
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.debug(b"sending %s command\n" % cmd)
q = [(b'cmd', cmd)]
Manuel Jacob
hidden: add support for --remote-hidden to HTTP peer...
r51313 if remotehidden:
q.append(('access-hidden', '1'))
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 headersize = 0
# Important: don't use self.capable() here or else you end up
# with infinite recursion when trying to look up capabilities
# for the first time.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 postargsok = caps is not None and b'httppostargs' in caps
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
# Send arguments via POST.
if postargsok and args:
strargs = urlreq.urlencode(sorted(args.items()))
if not data:
data = strargs
else:
if isinstance(data, bytes):
i = io.BytesIO(data)
i.length = len(data)
data = i
argsio = io.BytesIO(strargs)
argsio.length = len(strargs)
data = _multifile(argsio, data)
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 headers['X-HgArgs-Post'] = len(strargs)
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 elif args:
# Calling self.capable() can infinite loop if we are calling
# "capabilities". But that command should never accept wire
# protocol arguments. So this should never happen.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 assert cmd != b'capabilities'
httpheader = capablefn(b'httpheader')
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 if httpheader:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 headersize = int(httpheader.split(b',', 1)[0])
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
# Send arguments via HTTP headers.
if headersize > 0:
# The headers can typically carry more data than the URL.
Martin von Zweigbergk
wireprotopeer: clarify some variable names now that we allow snake_case...
r47209 encoded_args = urlreq.urlencode(sorted(args.items()))
Augie Fackler
formatting: blacken the codebase...
r43346 for header, value in encodevalueinheaders(
Martin von Zweigbergk
wireprotopeer: clarify some variable names now that we allow snake_case...
r47209 encoded_args, b'X-HgArg', headersize
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 headers[header] = value
# Send arguments via query string (Mercurial <1.9).
else:
q += sorted(args.items())
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 qs = b'?%s' % urlreq.urlencode(q)
cu = b"%s%s" % (repobaseurl, qs)
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 size = 0
safehasattr: drop usage in favor of hasattr...
r51821 if hasattr(data, 'length'):
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 size = data.length
elif data is not None:
size = len(data)
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 if data is not None and 'Content-Type' not in headers:
headers['Content-Type'] = 'application/mercurial-0.1'
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
# Tell the server we accept application/mercurial-0.2 and multiple
# compression formats if the server is capable of emitting those
# payloads.
Gregory Szorc
httppeer: only advertise partial-pull if capabilities are known...
r37574 # Note: Keep this set empty by default, as client advertisement of
# protocol parameters should only occur after the handshake.
protoparams = set()
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
mediatypes = set()
if caps is not None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 mt = capablefn(b'httpmediatype')
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 if mt:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 protoparams.add(b'0.1')
mediatypes = set(mt.split(b','))
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 protoparams.add(b'partial-pull')
Gregory Szorc
httppeer: only advertise partial-pull if capabilities are known...
r37574
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'0.2tx' in mediatypes:
protoparams.add(b'0.2')
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'0.2tx' in mediatypes and capablefn(b'compression'):
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 # We /could/ compare supported compression formats and prune
# non-mutually supported or error if nothing is mutually supported.
# For now, send the full list to the server and have it error.
Augie Fackler
formatting: blacken the codebase...
r43346 comps = [
e.wireprotosupport().name
for e in util.compengines.supportedwireengines(util.CLIENTROLE)
]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 protoparams.add(b'comp=%s' % b','.join(comps))
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
if protoparams:
Augie Fackler
formatting: blacken the codebase...
r43346 protoheaders = encodevalueinheaders(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b' '.join(sorted(protoparams)), b'X-HgProto', headersize or 1024
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 for header, value in protoheaders:
headers[header] = value
Gregory Szorc
httppeer: always add x-hg* headers to Vary header...
r37573
varyheaders = []
for header in headers:
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 if header.lower().startswith('x-hg'):
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567 varyheaders.append(header)
if varyheaders:
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 headers['Vary'] = ','.join(sorted(varyheaders))
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
req = requestbuilder(pycompat.strurl(cu), data, headers)
if data is not None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.debug(b"sending %d bytes\n" % size)
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 req.add_unredirected_header('Content-Length', '%d' % size)
Gregory Szorc
httppeer: extract code for creating a request into own function...
r37567
return req, cu, qs
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
httppeer: extract code for performing an HTTP request...
r37566 def sendrequest(ui, opener, req):
"""Send a prepared HTTP request.
Returns the response object.
"""
Boris Feld
httppeer: declare 'dbg' at the function level...
r38139 dbg = ui.debug
Matt Harbison
httppeer: move a variable to avoid a bad reference before assignment warning...
r52632 line = b'devel-peer-request: %s\n'
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if ui.debugflag and ui.configbool(b'devel', b'debug.peer-request'):
Augie Fackler
formatting: blacken the codebase...
r43346 dbg(
line
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 % b'%s %s'
Augie Fackler
formatting: blacken the codebase...
r43346 % (
pycompat.bytesurl(req.get_method()),
pycompat.bytesurl(req.get_full_url()),
)
)
Gregory Szorc
httppeer: extract code for performing an HTTP request...
r37566 hgargssize = None
for header, value in sorted(req.header_items()):
Augie Fackler
httppeer: no matter what Python 3 might think, http headers are bytes...
r37755 header = pycompat.bytesurl(header)
value = pycompat.bytesurl(value)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if header.startswith(b'X-hgarg-'):
Gregory Szorc
httppeer: extract code for performing an HTTP request...
r37566 if hgargssize is None:
hgargssize = 0
hgargssize += len(value)
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 dbg(line % b' %s %s' % (header, value))
Gregory Szorc
httppeer: extract code for performing an HTTP request...
r37566
if hgargssize is not None:
Augie Fackler
formatting: blacken the codebase...
r43346 dbg(
line
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 % b' %d bytes of commands arguments in headers'
Augie Fackler
formatting: blacken the codebase...
r43346 % hgargssize
)
Gregory Szorc
httppeer: inline simplified _reqdata()...
r49742 data = req.data
Augie Fackler
httppeer: work around API differences on urllib Request objects...
r37756 if data is not None:
Gregory Szorc
httppeer: extract code for performing an HTTP request...
r37566 length = getattr(data, 'length', None)
if length is None:
length = len(data)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 dbg(line % b' %d bytes of data' % length)
Gregory Szorc
httppeer: extract code for performing an HTTP request...
r37566
start = util.timer()
Martin von Zweigbergk
httppeer: fix use of uninitialized variable with devel logging...
r38521 res = None
Gregory Szorc
httppeer: move error handling and response wrapping into sendrequest...
r37568 try:
res = opener.open(req)
except urlerr.httperror as inst:
if inst.code == 401:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'authorization failed'))
Gregory Szorc
httppeer: move error handling and response wrapping into sendrequest...
r37568 raise
except httplib.HTTPException as inst:
Augie Fackler
formatting: blacken the codebase...
r43346 ui.debug(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'http error requesting %s\n'
urlutil: extract `url` related code from `util` into the new module...
r47669 % urlutil.hidepassword(req.get_full_url())
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: move error handling and response wrapping into sendrequest...
r37568 ui.traceback()
raise IOError(None, inst)
finally:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if ui.debugflag and ui.configbool(b'devel', b'debug.peer-request'):
Martin von Zweigbergk
httppeer: fix use of uninitialized variable with devel logging...
r38521 code = res.code if res else -1
Augie Fackler
formatting: blacken the codebase...
r43346 dbg(
line
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 % b' finished in %.4f seconds (%d)'
Augie Fackler
formatting: blacken the codebase...
r43346 % (util.timer() - start, code)
)
Gregory Szorc
httppeer: move error handling and response wrapping into sendrequest...
r37568
# Insert error handlers for common I/O failures.
Gregory Szorc
url: move _wraphttpresponse() from httpeer...
r40054 urlmod.wrapresponse(res)
Gregory Szorc
httppeer: extract code for performing an HTTP request...
r37566
return res
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
httppeer: detect redirect to URL without query string (issue5860)...
r37851 class RedirectedRepoError(error.RepoError):
def __init__(self, msg, respurl):
super(RedirectedRepoError, self).__init__(msg)
self.respurl = respurl
Augie Fackler
formatting: blacken the codebase...
r43346
Raphaël Gomès
exchangev2: remove it...
r49357 def parsev1commandresponse(ui, baseurl, requrl, qs, resp, compressible):
Gregory Szorc
httppeer: extract common response handling into own function...
r37569 # record the url we got redirected to
Gregory Szorc
httppeer: detect redirect to URL without query string (issue5860)...
r37851 redirected = False
Gregory Szorc
httppeer: extract common response handling into own function...
r37569 respurl = pycompat.bytesurl(resp.geturl())
if respurl.endswith(qs):
Augie Fackler
formatting: blacken the codebase...
r43346 respurl = respurl[: -len(qs)]
Gregory Szorc
httppeer: detect redirect to URL without query string (issue5860)...
r37851 qsdropped = False
else:
qsdropped = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if baseurl.rstrip(b'/') != respurl.rstrip(b'/'):
Gregory Szorc
httppeer: detect redirect to URL without query string (issue5860)...
r37851 redirected = True
Gregory Szorc
httppeer: extract common response handling into own function...
r37569 if not ui.quiet:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.warn(_(b'real URL is %s\n') % respurl)
Gregory Szorc
httppeer: extract common response handling into own function...
r37569
try:
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 proto = pycompat.bytesurl(resp.getheader('content-type', ''))
Gregory Szorc
httppeer: extract common response handling into own function...
r37569 except AttributeError:
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 proto = pycompat.bytesurl(resp.headers.get('content-type', ''))
Gregory Szorc
httppeer: extract common response handling into own function...
r37569
urlutil: extract `url` related code from `util` into the new module...
r47669 safeurl = urlutil.hidepassword(baseurl)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if proto.startswith(b'application/hg-error'):
Gregory Szorc
httppeer: extract common response handling into own function...
r37569 raise error.OutOfBandError(resp.read())
Gregory Szorc
httppeer: don't accept very old media types (BC)...
r37572
# Pre 1.0 versions of Mercurial used text/plain and
# application/hg-changegroup. We don't support such old servers.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not proto.startswith(b'application/mercurial-'):
urlutil: extract `url` related code from `util` into the new module...
r47669 ui.debug(b"requested URL: '%s'\n" % urlutil.hidepassword(requrl))
Augie Fackler
formatting: blacken the codebase...
r43346 msg = _(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"'%s' does not appear to be an hg repository:\n"
b"---%%<--- (%s)\n%s\n---%%<---\n"
) % (safeurl, proto or b'no content-type', resp.read(1024))
Gregory Szorc
httppeer: detect redirect to URL without query string (issue5860)...
r37851
# Some servers may strip the query string from the redirect. We
# raise a special error type so callers can react to this specially.
if redirected and qsdropped:
raise RedirectedRepoError(msg, respurl)
else:
raise error.RepoError(msg)
Gregory Szorc
httppeer: extract common response handling into own function...
r37569
Gregory Szorc
httppeer: don't accept very old media types (BC)...
r37572 try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 subtype = proto.split(b'-', 1)[1]
Gregory Szorc
httppeer: support protocol upgrade...
r37576
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 version_info = tuple([int(n) for n in subtype.split(b'.')])
Gregory Szorc
httppeer: don't accept very old media types (BC)...
r37572 except ValueError:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.RepoError(
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 _(b"'%s' sent a broken Content-Type header (%s)") % (safeurl, proto)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: extract common response handling into own function...
r37569
Gregory Szorc
httppeer: don't accept very old media types (BC)...
r37572 # TODO consider switching to a decompression reader that uses
# generators.
if version_info == (0, 1):
if compressible:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 resp = util.compengines[b'zlib'].decompressorreader(resp)
Gregory Szorc
httppeer: extract common response handling into own function...
r37569
Gregory Szorc
httppeer: don't accept very old media types (BC)...
r37572 elif version_info == (0, 2):
# application/mercurial-0.2 always identifies the compression
# engine in the payload header.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elen = struct.unpack(b'B', util.readexactly(resp, 1))[0]
av6
httppeer: use util.readexactly() to abort on incomplete responses...
r39520 ename = util.readexactly(resp, elen)
Gregory Szorc
httppeer: don't accept very old media types (BC)...
r37572 engine = util.compengines.forwiretype(ename)
Gregory Szorc
httppeer: extract common response handling into own function...
r37569
Gregory Szorc
httppeer: don't accept very old media types (BC)...
r37572 resp = engine.decompressorreader(resp)
else:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.RepoError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"'%s' uses newer protocol %s") % (safeurl, subtype)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: extract common response handling into own function...
r37569
Gregory Szorc
httppeer: support protocol upgrade...
r37576 return respurl, proto, resp
Gregory Szorc
httppeer: extract common response handling into own function...
r37569
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireproto: move version 1 peer functionality to standalone module (API)...
r37632 class httppeer(wireprotov1peer.wirepeer):
Manuel Jacob
pull: add --remote-hidden option and pass it through peer creation...
r51309 def __init__(
self, ui, path, url, opener, requestbuilder, caps, remotehidden=False
):
super().__init__(ui, path=path, remotehidden=remotehidden)
Gregory Szorc
httppeer: refactor how httppeer is created (API)...
r37024 self._url = url
Gregory Szorc
httppeer: perform capabilities request in makepeer()...
r37570 self._caps = caps
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.limitedarguments = caps is not None and b'httppostargs' not in caps
Gregory Szorc
httppeer: refactor how httppeer is created (API)...
r37024 self._urlopener = opener
Gregory Szorc
httppeer: move requestbuilder defaults into makepeer() argument...
r37565 self._requestbuilder = requestbuilder
Manuel Jacob
hidden: add support for --remote-hidden to HTTP peer...
r51313 self._remotehidden = remotehidden
Peter Arrenbrecht
peer: introduce real peer classes...
r17192
def __del__(self):
Gregory Szorc
httppeer: refactor how httppeer is created (API)...
r37024 for h in self._urlopener.handlers:
h.close()
getattr(h, "close_all", lambda: None)()
Peter Arrenbrecht
peer: introduce real peer classes...
r17192
Gregory Szorc
repository: port peer interfaces to zope.interface...
r37336 # Begin of ipeerconnection interface.
Gregory Szorc
httppeer: use peer interface...
r33804
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 def url(self):
peer: get the `path` object down to the httppeer...
r50654 return self.path.loc
Peter Arrenbrecht
peer: introduce real peer classes...
r17192
Gregory Szorc
httppeer: use peer interface...
r33804 def local(self):
return None
def canpush(self):
return True
Peter Arrenbrecht
peer: introduce real peer classes...
r17192
Gregory Szorc
httppeer: use peer interface...
r33804 def close(self):
Augie Fackler
http: work around custom http client classes that refuse extra attrs...
r40478 try:
Augie Fackler
formatting: blacken the codebase...
r43346 reqs, sent, recv = (
self._urlopener.requestscount,
self._urlopener.sentbytescount,
self._urlopener.receivedbytescount,
)
Augie Fackler
http: work around custom http client classes that refuse extra attrs...
r40478 except AttributeError:
return
Augie Fackler
formatting: blacken the codebase...
r43346 self.ui.note(
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'(sent %d HTTP requests and %d bytes; '
b'received %d bytes in responses)\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
% (reqs, sent, recv)
)
Peter Arrenbrecht
peer: introduce real peer classes...
r17192
Gregory Szorc
repository: port peer interfaces to zope.interface...
r37336 # End of ipeerconnection interface.
Gregory Szorc
httppeer: use peer interface...
r33804
Gregory Szorc
repository: port peer interfaces to zope.interface...
r37336 # Begin of ipeercommands interface.
Gregory Szorc
httppeer: use peer interface...
r33804
def capabilities(self):
Gregory Szorc
httppeer: make several instance attributes internal (API)...
r33671 return self._caps
Peter Arrenbrecht
peer: introduce real peer classes...
r17192
Mathias De Mare
clonebundles: add support for inline (streaming) clonebundles...
r51559 def _finish_inline_clone_bundle(self, stream):
# HTTP streams must hit the end to process the last empty
# chunk of Chunked-Encoding so the connection can be reused.
chunk = stream.read(1)
if chunk:
self._abort(error.ResponseError(_(b"unexpected response:"), chunk))
Gregory Szorc
repository: port peer interfaces to zope.interface...
r37336 # End of ipeercommands interface.
Gregory Szorc
httppeer: use peer interface...
r33804
Gregory Szorc
httppeer: do decompression inside _callstream...
r30464 def _callstream(self, cmd, _compressible=False, **args):
Pulkit Goyal
py3: handle keyword arguments correctly in httppeer.py...
r35360 args = pycompat.byteskwargs(args)
Gregory Szorc
httppeer: change logic around argument handling...
r36236
Augie Fackler
formatting: blacken the codebase...
r43346 req, cu, qs = makev1commandrequest(
self.ui,
self._requestbuilder,
self._caps,
self.capable,
self._url,
cmd,
args,
Manuel Jacob
hidden: add support for --remote-hidden to HTTP peer...
r51313 self._remotehidden,
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: advertise and support application/mercurial-0.2...
r30763
Gregory Szorc
httppeer: move error handling and response wrapping into sendrequest...
r37568 resp = sendrequest(self.ui, self._urlopener, req)
Gregory Szorc
httppeer: wrap HTTPResponse.read() globally...
r32002
Augie Fackler
formatting: blacken the codebase...
r43346 self._url, ct, resp = parsev1commandresponse(
self.ui, self._url, cu, qs, resp, _compressible
)
Gregory Szorc
httppeer: do decompression inside _callstream...
r30464
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 return resp
def _call(self, cmd, **args):
fp = self._callstream(cmd, **args)
try:
return fp.read()
finally:
# if using keepalive, allow connection to be reused
fp.close()
def _callpush(self, cmd, cg, **args):
# have to stream bundle to a temp file because we do not have
# http 1.1 chunked transfer.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 types = self.capable(b'unbundle')
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 types = types.split(b',')
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 except AttributeError:
# servers older than d1b16a746db6 will send 'unbundle' as a
# boolean capability. They only support headerless/uncompressed
# bundles.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 types = [b""]
Matt Harbison
httppeer: avoid another bad reference before assignment warning...
r52633
type = b""
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 for x in types:
Martin von Zweigbergk
bundle: move writebundle() from changegroup.py to bundle2.py (API)...
r28666 if x in bundle2.bundletypes:
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 type = x
break
Martin von Zweigbergk
bundle: move writebundle() from changegroup.py to bundle2.py (API)...
r28666 tempname = bundle2.writebundle(self.ui, cg, None, type)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp = httpconnection.httpsendfile(self.ui, tempname, b"rb")
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 headers = {'Content-Type': 'application/mercurial-0.1'}
Peter Arrenbrecht
peer: introduce real peer classes...
r17192
try:
Matt Mackall
httppeer: use try/except/finally
r25085 r = self._call(cmd, data=fp, headers=headers, **args)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 vals = r.split(b'\n', 1)
Matt Mackall
httppeer: use try/except/finally
r25085 if len(vals) < 2:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ResponseError(_(b"unexpected response:"), r)
Matt Mackall
httppeer: use try/except/finally
r25085 return vals
Augie Fackler
httppeer: explicitly catch urlerr.httperror and re-raise...
r36448 except urlerr.httperror:
# Catch and re-raise these so we don't try and treat them
# like generic socket errors. They lack any values in
# .args on Python 3 which breaks our socket.error block.
raise
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except socket.error as err:
Matt Mackall
httppeer: use try/except/finally
r25085 if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'push failed: %s') % err.args[1])
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(err.args[1])
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 finally:
fp.close()
os.unlink(tempname)
Pierre-Yves David
httppeer: support for _calltwowaystream...
r21074 def _calltwowaystream(self, cmd, fp, **args):
Matt Harbison
httppeer: simplify two-way stream cleanup...
r52631 # dump bundle to disk
fd, filename = pycompat.mkstemp(prefix=b"hg-bundle-", suffix=b".hg")
Pierre-Yves David
httppeer: support for _calltwowaystream...
r21074 try:
Augie Fackler
cleanup: remove pointless r-prefixes on double-quoted strings...
r43809 with os.fdopen(fd, "wb") as fh:
Pierre-Yves David
httppeer: support for _calltwowaystream...
r21074 d = fp.read(4096)
Martin von Zweigbergk
httppeer: use context manager when writing temporary bundle to send...
r43118 while d:
fh.write(d)
d = fp.read(4096)
Pierre-Yves David
httppeer: support for _calltwowaystream...
r21074 # start http push
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 with httpconnection.httpsendfile(self.ui, filename, b"rb") as fp_:
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 headers = {'Content-Type': 'application/mercurial-0.1'}
Martin von Zweigbergk
httppeer: use context manager when reading temporary bundle to send...
r43119 return self._callstream(cmd, data=fp_, headers=headers, **args)
Pierre-Yves David
httppeer: support for _calltwowaystream...
r21074 finally:
Matt Harbison
httppeer: simplify two-way stream cleanup...
r52631 os.unlink(filename)
Pierre-Yves David
httppeer: support for _calltwowaystream...
r21074
Pierre-Yves David
wireproto: drop the _decompress method in favor a new call type...
r20905 def _callcompressable(self, cmd, **args):
Gregory Szorc
httppeer: do decompression inside _callstream...
r30464 return self._callstream(cmd, _compressible=True, **args)
Peter Arrenbrecht
peer: introduce real peer classes...
r17192
Mads Kiilerich
httppeer: reintroduce _abort that accidentally was removed in 167047ba3cfa...
r21188 def _abort(self, exception):
raise exception
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
cleanup: directly use concurrent.futures instead of via pycompat...
r49690 class queuedcommandfuture(futures.Future):
Gregory Szorc
httppeer: implement command executor for version 2 peer...
r37669 """Wraps result() on command futures to trigger submission on call."""
def result(self, timeout=None):
if self.done():
Augie Fackler
cleanup: directly use concurrent.futures instead of via pycompat...
r49690 return futures.Future.result(self, timeout)
Gregory Szorc
httppeer: implement command executor for version 2 peer...
r37669
self._peerexecutor.sendcommands()
# sendcommands() will restore the original __class__ and self.result
# will resolve to Future.result.
return self.result(timeout)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
httppeer: perform capabilities request in makepeer()...
r37570 def performhandshake(ui, url, opener, requestbuilder):
# The handshake is a request to the capabilities command.
caps = None
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
httppeer: perform capabilities request in makepeer()...
r37570 def capable(x):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'should not be called')
Gregory Szorc
httppeer: perform capabilities request in makepeer()...
r37570
Gregory Szorc
httppeer: support protocol upgrade...
r37576 args = {}
Augie Fackler
formatting: blacken the codebase...
r43346 req, requrl, qs = makev1commandrequest(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui, requestbuilder, caps, capable, url, b'capabilities', args
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: perform capabilities request in makepeer()...
r37570 resp = sendrequest(ui, opener, req)
Gregory Szorc
httppeer: detect redirect to URL without query string (issue5860)...
r37851 # The server may redirect us to the repo root, stripping the
# ?cmd=capabilities query string from the URL. The server would likely
# return HTML in this case and ``parsev1commandresponse()`` would raise.
# We catch this special case and re-issue the capabilities request against
# the new URL.
#
# We should ideally not do this, as a redirect that drops the query
# string from the URL is arguably a server bug. (Garbage in, garbage out).
# However, Mercurial clients for several years appeared to handle this
# issue without behavior degradation. And according to issue 5860, it may
# be a longstanding bug in some server implementations. So we allow a
# redirect that drops the query string to "just work."
try:
Augie Fackler
formatting: blacken the codebase...
r43346 respurl, ct, resp = parsev1commandresponse(
Raphaël Gomès
exchangev2: remove it...
r49357 ui, url, requrl, qs, resp, compressible=False
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: detect redirect to URL without query string (issue5860)...
r37851 except RedirectedRepoError as e:
Augie Fackler
formatting: blacken the codebase...
r43346 req, requrl, qs = makev1commandrequest(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui, requestbuilder, caps, capable, e.respurl, b'capabilities', args
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: detect redirect to URL without query string (issue5860)...
r37851 resp = sendrequest(ui, opener, req)
Augie Fackler
formatting: blacken the codebase...
r43346 respurl, ct, resp = parsev1commandresponse(
Raphaël Gomès
exchangev2: remove it...
r49357 ui, url, requrl, qs, resp, compressible=False
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: perform capabilities request in makepeer()...
r37570
try:
Gregory Szorc
httppeer: support protocol upgrade...
r37576 rawdata = resp.read()
Gregory Szorc
httppeer: perform capabilities request in makepeer()...
r37570 finally:
resp.close()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not ct.startswith(b'application/mercurial-'):
raise error.ProgrammingError(b'unexpected content-type: %s' % ct)
Gregory Szorc
httppeer: support protocol upgrade...
r37576
Raphaël Gomès
exchangev2: remove it...
r49357 info = {b'v1capabilities': set(rawdata.split())}
Gregory Szorc
httppeer: support protocol upgrade...
r37576
return respurl, info
Gregory Szorc
httppeer: perform capabilities request in makepeer()...
r37570
Augie Fackler
formatting: blacken the codebase...
r43346
Manuel Jacob
pull: add --remote-hidden option and pass it through peer creation...
r51309 def _make_peer(
ui, path, opener=None, requestbuilder=urlreq.request, remotehidden=False
):
Gregory Szorc
httppeer: move requestbuilder defaults into makepeer() argument...
r37565 """Construct an appropriate HTTP peer instance.
Gregory Szorc
httppeer: allow opener to be passed to makepeer()...
r37571 ``opener`` is an ``url.opener`` that should be used to establish
connections, perform HTTP requests.
Gregory Szorc
httppeer: move requestbuilder defaults into makepeer() argument...
r37565 ``requestbuilder`` is the type used for constructing HTTP requests.
It exists as an argument so extensions can override the default.
"""
peer: get the `path` object down to the httppeer...
r50654 if path.url.query or path.url.fragment:
msg = _(b'unsupported URL component: "%s"')
msg %= path.url.query or path.url.fragment
raise error.Abort(msg)
Gregory Szorc
httppeer: refactor how httppeer is created (API)...
r37024
# urllib cannot handle URLs with embedded user or passwd.
peer: get the `path` object down to the httppeer...
r50654 url, authinfo = path.url.authinfo()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.debug(b'using %s\n' % url)
Gregory Szorc
httppeer: refactor how httppeer is created (API)...
r37024
Gregory Szorc
httppeer: allow opener to be passed to makepeer()...
r37571 opener = opener or urlmod.opener(ui, authinfo)
Gregory Szorc
httppeer: refactor how httppeer is created (API)...
r37024
Gregory Szorc
httppeer: support protocol upgrade...
r37576 respurl, info = performhandshake(ui, url, opener, requestbuilder)
Augie Fackler
formatting: blacken the codebase...
r43346 return httppeer(
Manuel Jacob
pull: add --remote-hidden option and pass it through peer creation...
r51309 ui,
path,
respurl,
opener,
requestbuilder,
info[b'v1capabilities'],
remotehidden=remotehidden,
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: refactor how httppeer is created (API)...
r37024
Manuel Jacob
pull: add --remote-hidden option and pass it through peer creation...
r51309 def make_peer(
ui, path, create, intents=None, createopts=None, remotehidden=False
):
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 if create:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'cannot create new http repository'))
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 try:
peer: get the `path` object down to the httppeer...
r50654 if path.url.scheme == b'https' and not urlmod.has_https:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 _(b'Python support for SSL and HTTPS is not installed')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
httppeer: remove support for connecting to <0.9.1 servers (BC)...
r35902
Manuel Jacob
pull: add --remote-hidden option and pass it through peer creation...
r51309 inst = _make_peer(ui, path, remotehidden=remotehidden)
Gregory Szorc
httppeer: remove support for connecting to <0.9.1 servers (BC)...
r35902
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 return inst
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.RepoError as httpexception:
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 try:
av6
httppeer: fix static-http: scheme autodetection (issue6833)...
r51883 path = path.copy(new_raw_location=b"static-" + path.rawloc)
r = statichttprepo.make_peer(ui, path, create)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.note(_(b'(falling back to static-http)\n'))
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 return r
except error.RepoError:
Augie Fackler
formatting: blacken the codebase...
r43346 raise httpexception # use the original http RepoError instead