mercurial/wireprotoframing.py
changeset 37056 861e9d37e56e
parent 37055 61393f888dfe
child 37057 2ec1fb9de638
--- a/mercurial/wireprotoframing.py	Wed Mar 14 13:57:52 2018 -0700
+++ b/mercurial/wireprotoframing.py	Wed Mar 14 14:01:16 2018 -0700
@@ -308,10 +308,24 @@
     wantframe
        Indicates that nothing of interest happened and the server is waiting on
        more frames from the client before anything interesting can be done.
+
+    noop
+       Indicates no additional action is required.
     """
 
-    def __init__(self):
+    def __init__(self, deferoutput=False):
+        """Construct a new server reactor.
+
+        ``deferoutput`` can be used to indicate that no output frames should be
+        instructed to be sent until input has been exhausted. In this mode,
+        events that would normally generate output frames (such as a command
+        response being ready) will instead defer instructing the consumer to
+        send those frames. This is useful for half-duplex transports where the
+        sender cannot receive until all data has been transmitted.
+        """
+        self._deferoutput = deferoutput
         self._state = 'idle'
+        self._bufferedframegens = []
         self._activecommand = None
         self._activeargs = None
         self._activedata = None
@@ -344,8 +358,33 @@
 
         The raw bytes response is passed as an argument.
         """
+        framegen = createbytesresponseframesfrombytes(data)
+
+        if self._deferoutput:
+            self._bufferedframegens.append(framegen)
+            return 'noop', {}
+        else:
+            return 'sendframes', {
+                'framegen': framegen,
+            }
+
+    def oninputeof(self):
+        """Signals that end of input has been received.
+
+        No more frames will be received. All pending activity should be
+        completed.
+        """
+        if not self._deferoutput or not self._bufferedframegens:
+            return 'noop', {}
+
+        # If we buffered all our responses, emit those.
+        def makegen():
+            for gen in self._bufferedframegens:
+                for frame in gen:
+                    yield frame
+
         return 'sendframes', {
-            'framegen': createbytesresponseframesfrombytes(data),
+            'framegen': makegen(),
         }
 
     def onapplicationerror(self, msg):