D3234: httppeer: extract code for creating a request into own function

2018-04-11 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG66d1001e1500: httppeer: extract code for creating a request 
into own function (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D3234?vs=7952&id=7995

REVISION DETAIL
  https://phab.mercurial-scm.org/D3234

AFFECTED FILES
  mercurial/httppeer.py

CHANGE DETAILS

diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -138,6 +138,119 @@
 f.seek(0)
 self._index = 0
 
+def makev1commandrequest(ui, requestbuilder, caps, capablefn,
+ repobaseurl, cmd, args):
+"""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.
+"""
+if cmd == 'pushkey':
+args['data'] = ''
+data = args.pop('data', None)
+headers = args.pop('headers', {})
+
+ui.debug("sending %s command\n" % cmd)
+q = [('cmd', cmd)]
+headersize = 0
+varyheaders = []
+# 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.
+postargsok = caps is not None and 'httppostargs' in caps
+
+# 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)
+headers[r'X-HgArgs-Post'] = len(strargs)
+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.
+assert cmd != 'capabilities'
+httpheader = capablefn('httpheader')
+if httpheader:
+headersize = int(httpheader.split(',', 1)[0])
+
+# Send arguments via HTTP headers.
+if headersize > 0:
+# The headers can typically carry more data than the URL.
+encargs = urlreq.urlencode(sorted(args.items()))
+for header, value in encodevalueinheaders(encargs, 'X-HgArg',
+  headersize):
+headers[header] = value
+varyheaders.append(header)
+# Send arguments via query string (Mercurial <1.9).
+else:
+q += sorted(args.items())
+
+qs = '?%s' % urlreq.urlencode(q)
+cu = "%s%s" % (repobaseurl, qs)
+size = 0
+if util.safehasattr(data, 'length'):
+size = data.length
+elif data is not None:
+size = len(data)
+if data is not None and r'Content-Type' not in headers:
+headers[r'Content-Type'] = r'application/mercurial-0.1'
+
+# Tell the server we accept application/mercurial-0.2 and multiple
+# compression formats if the server is capable of emitting those
+# payloads.
+protoparams = {'partial-pull'}
+
+mediatypes = set()
+if caps is not None:
+mt = capablefn('httpmediatype')
+if mt:
+protoparams.add('0.1')
+mediatypes = set(mt.split(','))
+
+if '0.2tx' in mediatypes:
+protoparams.add('0.2')
+
+if '0.2tx' in mediatypes and capablefn('compression'):
+# 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.
+comps = [e.wireprotosupport().name for e in
+ util.compengines.supportedwireengines(util.CLIENTROLE)]
+protoparams.add('comp=%s' % ','.join(comps))
+
+if protoparams:
+protoheaders = encodevalueinheaders(' '.join(sorted(protoparams)),
+'X-HgProto',
+headersize or 1024)
+for header, value in protoheaders:
+headers[header] = value
+varyheaders.append(header)
+
+if varyheaders:
+headers[r'Vary'] = r','.join(varyheaders)
+
+req = requestbuilder(pycompat.strurl(cu), data, headers)
+
+if data is not None:
+ui.debug("sending %d bytes\n" % size)
+req.add_unredirected_header(r'Content-Length', r'%d' % size)
+
+return req, cu, qs
+
 def sendrequest(ui, opener, req):
 """Send a prepared HTTP request.
 
@@ -228,104 +

D3234: httppeer: extract code for creating a request into own function

2018-04-10 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Some of this feels awkward, such as having to pass in a function
  to evaluate a capability. And this code is generally pretty difficult
  to read. I didn't want to perform too much refactoring as part of
  the code move since it would make review more difficult.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D3234

AFFECTED FILES
  mercurial/httppeer.py

CHANGE DETAILS

diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -138,6 +138,119 @@
 f.seek(0)
 self._index = 0
 
+def makev1commandrequest(ui, requestbuilder, caps, capablefn,
+ repobaseurl, cmd, args):
+"""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.
+"""
+if cmd == 'pushkey':
+args['data'] = ''
+data = args.pop('data', None)
+headers = args.pop('headers', {})
+
+ui.debug("sending %s command\n" % cmd)
+q = [('cmd', cmd)]
+headersize = 0
+varyheaders = []
+# 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.
+postargsok = caps is not None and 'httppostargs' in caps
+
+# 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)
+headers[r'X-HgArgs-Post'] = len(strargs)
+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.
+assert cmd != 'capabilities'
+httpheader = capablefn('httpheader')
+if httpheader:
+headersize = int(httpheader.split(',', 1)[0])
+
+# Send arguments via HTTP headers.
+if headersize > 0:
+# The headers can typically carry more data than the URL.
+encargs = urlreq.urlencode(sorted(args.items()))
+for header, value in encodevalueinheaders(encargs, 'X-HgArg',
+  headersize):
+headers[header] = value
+varyheaders.append(header)
+# Send arguments via query string (Mercurial <1.9).
+else:
+q += sorted(args.items())
+
+qs = '?%s' % urlreq.urlencode(q)
+cu = "%s%s" % (repobaseurl, qs)
+size = 0
+if util.safehasattr(data, 'length'):
+size = data.length
+elif data is not None:
+size = len(data)
+if data is not None and r'Content-Type' not in headers:
+headers[r'Content-Type'] = r'application/mercurial-0.1'
+
+# Tell the server we accept application/mercurial-0.2 and multiple
+# compression formats if the server is capable of emitting those
+# payloads.
+protoparams = {'partial-pull'}
+
+mediatypes = set()
+if caps is not None:
+mt = capablefn('httpmediatype')
+if mt:
+protoparams.add('0.1')
+mediatypes = set(mt.split(','))
+
+if '0.2tx' in mediatypes:
+protoparams.add('0.2')
+
+if '0.2tx' in mediatypes and capablefn('compression'):
+# 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.
+comps = [e.wireprotosupport().name for e in
+ util.compengines.supportedwireengines(util.CLIENTROLE)]
+protoparams.add('comp=%s' % ','.join(comps))
+
+if protoparams:
+protoheaders = encodevalueinheaders(' '.join(sorted(protoparams)),
+'X-HgProto',
+headersize or 1024)
+for header, value in protoheaders:
+headers[header] = value
+varyheaders.append(header)
+
+if varyheaders:
+headers[r'Vary'] = r','.join(varyheaders)
+
+req = requestbuilder(pycompat.strurl(cu), data, headers)
+
+if data is not None:
+ui.debug("sending %d bytes\n" % size)
+req.add_unredirected_header(r'Content-Length', r'%d' % size)
+
+retu