Patchwork [1,of,6,zstd-wireproto,V3] httppeer: extract code for HTTP header spanning

login
register
mail settings
Submitter Gregory Szorc
Date Dec. 24, 2016, 10:33 p.m.
Message ID <13890ebe4322e735a589.1482618785@gps-mbp.local>
Download mbox | patch
Permalink /patch/18031/
State Accepted
Headers show

Comments

Gregory Szorc - Dec. 24, 2016, 10:33 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1482615962 25200
#      Sat Dec 24 14:46:02 2016 -0700
# Node ID 13890ebe4322e735a589409b5d62da9555fae7d2
# Parent  64a75655b98855dc80a67f7b74f758281e540453
httppeer: extract code for HTTP header spanning

A second consumer of HTTP header spanning will soon be introduced.
Factor out the code to do this so it can be reused.

Patch

diff --git a/mercurial/hgweb/protocol.py b/mercurial/hgweb/protocol.py
--- a/mercurial/hgweb/protocol.py
+++ b/mercurial/hgweb/protocol.py
@@ -24,8 +24,22 @@  urlreq = util.urlreq
 
 HGTYPE = 'application/mercurial-0.1'
 HGERRTYPE = 'application/hg-error'
 
+def decodevaluefromheaders(req, headerprefix):
+    """Decode a long value from multiple HTTP request headers."""
+    chunks = []
+    i = 1
+    while True:
+        v = req.env.get('HTTP_%s_%d' % (
+            headerprefix.upper().replace('-', '_'), i))
+        if v is None:
+            break
+        chunks.append(v)
+        i += 1
+
+    return ''.join(chunks)
+
 class webproto(wireproto.abstractserverproto):
     def __init__(self, req, ui):
         self.req = req
         self.response = ''
@@ -52,17 +66,11 @@  class webproto(wireproto.abstractserverp
         if postlen:
             args.update(cgi.parse_qs(
                 self.req.read(postlen), keep_blank_values=True))
             return args
-        chunks = []
-        i = 1
-        while True:
-            h = self.req.env.get('HTTP_X_HGARG_' + str(i))
-            if h is None:
-                break
-            chunks += [h]
-            i += 1
-        args.update(cgi.parse_qs(''.join(chunks), keep_blank_values=True))
+
+        argvalue = decodevaluefromheaders(self.req, 'X-HgArg')
+        args.update(cgi.parse_qs(argvalue, keep_blank_values=True))
         return args
     def getfile(self, fp):
         length = int(self.req.env['CONTENT_LENGTH'])
         for s in util.filechunkiter(self.req, limit=length):
diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -52,8 +52,28 @@  def decompressresponse(response, engine)
 
     reader.__class__ = readerproxy
     return reader
 
+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.
+
+    Returns an iterable of 2-tuples consisting of header names and values.
+    """
+    fmt = header + '-%s'
+    valuelen = limit - len(fmt % '000') - len(': \r\n')
+    result = []
+
+    n = 0
+    for i in xrange(0, len(value), valuelen):
+        n += 1
+        result.append((fmt % str(n), value[i:i + valuelen]))
+
+    return result
+
 class httppeer(wireproto.wirepeer):
     def __init__(self, ui, path):
         self.path = path
         self.caps = None
@@ -134,15 +154,11 @@  class httppeer(wireproto.wirepeer):
                     headersize = int(httpheader.split(',', 1)[0])
             if headersize > 0:
                 # The headers can typically carry more data than the URL.
                 encargs = urlreq.urlencode(sorted(args.items()))
-                headerfmt = 'X-HgArg-%s'
-                contentlen = headersize - len(headerfmt % '000' + ': \r\n')
-                headernum = 0
-                for i in xrange(0, len(encargs), contentlen):
-                    headernum += 1
-                    header = headerfmt % str(headernum)
-                    headers[header] = encargs[i:i + contentlen]
+                for header, value in encodevalueinheaders(encargs, 'X-HgArg',
+                                                          headersize):
+                    headers[header] = value
                     varyheaders.append(header)
             else:
                 q += sorted(args.items())
         qs = '?%s' % urlreq.urlencode(q)