mercurial/wireprotoframing.py
changeset 40025 b099e6032f38
parent 40024 86b22a4cfab1
child 40125 e2fe1074024c
--- a/mercurial/wireprotoframing.py	Wed Sep 26 15:02:19 2018 -0700
+++ b/mercurial/wireprotoframing.py	Wed Sep 26 18:07:55 2018 -0700
@@ -21,6 +21,7 @@
 from . import (
     encoding,
     error,
+    pycompat,
     util,
     wireprototypes,
 )
@@ -429,6 +430,26 @@
                             flags=FLAG_COMMAND_RESPONSE_EOS,
                             payload=b'')
 
+def createalternatelocationresponseframe(stream, requestid, location):
+    data = {
+        b'status': b'redirect',
+        b'location': {
+            b'url': location.url,
+            b'mediatype': location.mediatype,
+        }
+    }
+
+    for a in (r'size', r'fullhashes', r'fullhashseed', r'serverdercerts',
+              r'servercadercerts'):
+        value = getattr(location, a)
+        if value is not None:
+            data[b'location'][pycompat.bytestr(a)] = value
+
+    return stream.makeframe(requestid=requestid,
+                            typeid=FRAME_TYPE_COMMAND_RESPONSE,
+                            flags=FLAG_COMMAND_RESPONSE_CONTINUATION,
+                            payload=b''.join(cborutil.streamencode(data)))
+
 def createcommanderrorresponse(stream, requestid, message, args=None):
     # TODO should this be using a list of {'msg': ..., 'args': {}} so atom
     # formatting works consistently?
@@ -813,6 +834,7 @@
 
         def sendframes():
             emitted = False
+            alternatelocationsent = False
             emitter = bufferingcommandresponseemitter(stream, requestid)
             while True:
                 try:
@@ -841,6 +863,25 @@
                     break
 
                 try:
+                    # Alternate location responses can only be the first and
+                    # only object in the output stream.
+                    if isinstance(o, wireprototypes.alternatelocationresponse):
+                        if emitted:
+                            raise error.ProgrammingError(
+                                'alternatelocationresponse seen after initial '
+                                'output object')
+
+                        yield createalternatelocationresponseframe(
+                            stream, requestid, o)
+
+                        alternatelocationsent = True
+                        emitted = True
+                        continue
+
+                    if alternatelocationsent:
+                        raise error.ProgrammingError(
+                            'object follows alternatelocationresponse')
+
                     if not emitted:
                         yield createcommandresponseokframe(stream, requestid)
                         emitted = True
@@ -977,6 +1018,7 @@
             'requestid': requestid,
             'command': request[b'name'],
             'args': request[b'args'],
+            'redirect': request.get(b'redirect'),
             'data': entry['data'].getvalue() if entry['data'] else None,
         }