Show More
@@ -2777,22 +2777,23 b' def debugwireproto(ui, repo, path=None, ' | |||
|
2777 | 2777 | syntax. |
|
2778 | 2778 | |
|
2779 | 2779 | A frame is composed as a type, flags, and payload. These can be parsed |
|
2780 | from a string of the form ``<requestid> <type> <flags> <payload>``. That is, | |
|
2781 | 4 space-delimited strings. | |
|
2782 | ||
|
2783 | ``payload`` is the simplest: it is evaluated as a Python byte string | |
|
2784 | literal. | |
|
2785 | ||
|
2786 | ``requestid`` is an integer defining the request identifier. | |
|
2780 | from a string of the form: | |
|
2781 | ||
|
2782 | <request-id> <stream-id> <stream-flags> <type> <flags> <payload> | |
|
2783 | ||
|
2784 | ``request-id`` and ``stream-id`` are integers defining the request and | |
|
2785 | stream identifiers. | |
|
2787 | 2786 | |
|
2788 | 2787 | ``type`` can be an integer value for the frame type or the string name |
|
2789 | 2788 | of the type. The strings are defined in ``wireprotoframing.py``. e.g. |
|
2790 | 2789 | ``command-name``. |
|
2791 | 2790 | |
|
2792 |
``flags`` |
|
|
2793 |
(and there can be just one) can be an integer |
|
|
2794 | specified frame type. Values are resolved to integers and then bitwise | |
|
2795 | OR'd together. | |
|
2791 | ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag | |
|
2792 | components. Each component (and there can be just one) can be an integer | |
|
2793 | or a flag name for stream flags or frame flags, respectively. Values are | |
|
2794 | resolved to integers and then bitwise OR'd together. | |
|
2795 | ||
|
2796 | ``payload`` is is evaluated as a Python byte string literal. | |
|
2796 | 2797 | """ |
|
2797 | 2798 | opts = pycompat.byteskwargs(opts) |
|
2798 | 2799 |
@@ -489,28 +489,37 b' To operate the protocol, a bi-directiona' | |||
|
489 | 489 | ordered sends and receives is required. That is, each peer has one pipe |
|
490 | 490 | for sending data and another for receiving. |
|
491 | 491 | |
|
492 | All data is read and written in atomic units called *frames*. These | |
|
493 | are conceptually similar to TCP packets. Higher-level functionality | |
|
494 | is built on the exchange and processing of frames. | |
|
495 | ||
|
496 | All frames are associated with a *stream*. A *stream* provides a | |
|
497 | unidirectional grouping of frames. Streams facilitate two goals: | |
|
498 | content encoding and parallelism. There is a dedicated section on | |
|
499 | streams below. | |
|
500 | ||
|
492 | 501 | The protocol is request-response based: the client issues requests to |
|
493 | 502 | the server, which issues replies to those requests. Server-initiated |
|
494 | 503 | messaging is not currently supported, but this specification carves |
|
495 | 504 | out room to implement it. |
|
496 | 505 | |
|
497 | All data is read and written in atomic units called *frames*. These | |
|
498 | are conceptually similar to TCP packets. Higher-level functionality | |
|
499 | is built on the exchange and processing of frames. | |
|
500 | ||
|
501 | 506 | All frames are associated with a numbered request. Frames can thus |
|
502 | 507 | be logically grouped by their request ID. |
|
503 | 508 | |
|
504 |
Frames begin with a |
|
|
509 | Frames begin with an 8 octet header followed by a variable length | |
|
505 | 510 | payload:: |
|
506 | 511 | |
|
507 | +-----------------------------------------------+ | |
|
508 | | Length (24) | | |
|
509 |
+--------------------------------- |
|
|
510 | | Request ID (16) | | |
|
511 | +----------+-----------+----------+ | |
|
512 |
| |
|
|
513 | +==========+===========+========================================| | |
|
512 | +------------------------------------------------+ | |
|
513 | | Length (24) | | |
|
514 | +--------------------------------+---------------+ | |
|
515 | | Request ID (16) | Stream ID (8) | | |
|
516 | +------------------+-------------+---------------+ | |
|
517 | | Stream Flags (8) | | |
|
518 | +-----------+------+ | |
|
519 | | Type (4) | | |
|
520 | +-----------+ | |
|
521 | | Flags (4) | | |
|
522 | +===========+===================================================| | |
|
514 | 523 | | Frame Payload (0...) ... |
|
515 | 524 | +---------------------------------------------------------------+ |
|
516 | 525 | |
@@ -518,7 +527,9 b' The length of the frame payload is expre' | |||
|
518 | 527 | little endian integer. Values larger than 65535 MUST NOT be used unless |
|
519 | 528 | given permission by the server as part of the negotiated capabilities |
|
520 | 529 | during the handshake. The frame header is not part of the advertised |
|
521 | frame length. | |
|
530 | frame length. The payload length is the over-the-wire length. If there | |
|
531 | is content encoding applied to the payload as part of the frame's stream, | |
|
532 | the length is the output of that content encoding, not the input. | |
|
522 | 533 | |
|
523 | 534 | The 16-bit ``Request ID`` field denotes the integer request identifier, |
|
524 | 535 | stored as an unsigned little endian integer. Odd numbered requests are |
@@ -529,7 +540,16 b' response to client-initiated requests. I' | |||
|
529 | 540 | start ordering request identifiers at ``1`` and ``0``, increment by |
|
530 | 541 | ``2``, and wrap around if all available numbers have been exhausted. |
|
531 | 542 | |
|
532 |
The |
|
|
543 | The 8-bit ``Stream ID`` field denotes the stream that the frame is | |
|
544 | associated with. Frames belonging to a stream may have content | |
|
545 | encoding applied and the receiver may need to decode the raw frame | |
|
546 | payload to obtain the original data. Odd numbered IDs are | |
|
547 | client-initiated. Even numbered IDs are server-initiated. | |
|
548 | ||
|
549 | The 8-bit ``Stream Flags`` field defines stream processing semantics. | |
|
550 | See the section on streams below. | |
|
551 | ||
|
552 | The 4-bit ``Type`` field denotes the type of frame being sent. | |
|
533 | 553 | |
|
534 | 554 | The 4-bit ``Flags`` field defines special, per-type attributes for |
|
535 | 555 | the frame. |
@@ -720,6 +740,126 b' All textual data encoded in these frames' | |||
|
720 | 740 | The last atom in the frame SHOULD end with a newline (``\n``). If it |
|
721 | 741 | doesn't, clients MAY add a newline to facilitate immediate printing. |
|
722 | 742 | |
|
743 | Stream Encoding Settings (``0x08``) | |
|
744 | ----------------------------------- | |
|
745 | ||
|
746 | This frame type holds information defining the content encoding | |
|
747 | settings for a *stream*. | |
|
748 | ||
|
749 | This frame type is likely consumed by the protocol layer and is not | |
|
750 | passed on to applications. | |
|
751 | ||
|
752 | This frame type MUST ONLY occur on frames having the *Beginning of Stream* | |
|
753 | ``Stream Flag`` set. | |
|
754 | ||
|
755 | The payload of this frame defines what content encoding has (possibly) | |
|
756 | been applied to the payloads of subsequent frames in this stream. | |
|
757 | ||
|
758 | The payload begins with an 8-bit integer defining the length of the | |
|
759 | encoding *profile*, followed by the string name of that profile, which | |
|
760 | must be an ASCII string. All bytes that follow can be used by that | |
|
761 | profile for supplemental settings definitions. See the section below | |
|
762 | on defined encoding profiles. | |
|
763 | ||
|
764 | Stream States and Flags | |
|
765 | ----------------------- | |
|
766 | ||
|
767 | Streams can be in two states: *open* and *closed*. An *open* stream | |
|
768 | is active and frames attached to that stream could arrive at any time. | |
|
769 | A *closed* stream is not active. If a frame attached to a *closed* | |
|
770 | stream arrives, that frame MUST have an appropriate stream flag | |
|
771 | set indicating beginning of stream. All streams are in the *closed* | |
|
772 | state by default. | |
|
773 | ||
|
774 | The ``Stream Flags`` field denotes a set of bit flags for defining | |
|
775 | the relationship of this frame within a stream. The following flags | |
|
776 | are defined: | |
|
777 | ||
|
778 | 0x01 | |
|
779 | Beginning of stream. The first frame in the stream MUST set this | |
|
780 | flag. When received, the ``Stream ID`` this frame is attached to | |
|
781 | becomes ``open``. | |
|
782 | ||
|
783 | 0x02 | |
|
784 | End of stream. The last frame in a stream MUST set this flag. When | |
|
785 | received, the ``Stream ID`` this frame is attached to becomes | |
|
786 | ``closed``. Any content encoding context associated with this stream | |
|
787 | can be destroyed after processing the payload of this frame. | |
|
788 | ||
|
789 | 0x04 | |
|
790 | Apply content encoding. When set, any content encoding settings | |
|
791 | defined by the stream should be applied when attempting to read | |
|
792 | the frame. When not set, the frame payload isn't encoded. | |
|
793 | ||
|
794 | Streams | |
|
795 | ------- | |
|
796 | ||
|
797 | Streams - along with ``Request IDs`` - facilitate grouping of frames. | |
|
798 | But the purpose of each is quite different and the groupings they | |
|
799 | constitute are independent. | |
|
800 | ||
|
801 | A ``Request ID`` is essentially a tag. It tells you which logical | |
|
802 | request a frame is associated with. | |
|
803 | ||
|
804 | A *stream* is a sequence of frames grouped for the express purpose | |
|
805 | of applying a stateful encoding or for denoting sub-groups of frames. | |
|
806 | ||
|
807 | Unlike ``Request ID``s which span the request and response, a stream | |
|
808 | is unidirectional and stream IDs are independent from client to | |
|
809 | server. | |
|
810 | ||
|
811 | There is no strict hierarchical relationship between ``Request IDs`` | |
|
812 | and *streams*. A stream can contain frames having multiple | |
|
813 | ``Request IDs``. Frames belonging to the same ``Request ID`` can | |
|
814 | span multiple streams. | |
|
815 | ||
|
816 | One goal of streams is to facilitate content encoding. A stream can | |
|
817 | define an encoding to be applied to frame payloads. For example, the | |
|
818 | payload transmitted over the wire may contain output from a | |
|
819 | zstandard compression operation and the receiving end may decompress | |
|
820 | that payload to obtain the original data. | |
|
821 | ||
|
822 | The other goal of streams is to facilitate concurrent execution. For | |
|
823 | example, a server could spawn 4 threads to service a request that can | |
|
824 | be easily parallelized. Each of those 4 threads could write into its | |
|
825 | own stream. Those streams could then in turn be delivered to 4 threads | |
|
826 | on the receiving end, with each thread consuming its stream in near | |
|
827 | isolation. The *main* thread on both ends merely does I/O and | |
|
828 | encodes/decodes frame headers: the bulk of the work is done by worker | |
|
829 | threads. | |
|
830 | ||
|
831 | In addition, since content encoding is defined per stream, each | |
|
832 | *worker thread* could perform potentially CPU bound work concurrently | |
|
833 | with other threads. This approach of applying encoding at the | |
|
834 | sub-protocol / stream level eliminates a potential resource constraint | |
|
835 | on the protocol stream as a whole (it is common for the throughput of | |
|
836 | a compression engine to be smaller than the throughput of a network). | |
|
837 | ||
|
838 | Having multiple streams - each with their own encoding settings - also | |
|
839 | facilitates the use of advanced data compression techniques. For | |
|
840 | example, a transmitter could see that it is generating data faster | |
|
841 | and slower than the receiving end is consuming it and adjust its | |
|
842 | compression settings to trade CPU for compression ratio accordingly. | |
|
843 | ||
|
844 | While streams can define a content encoding, not all frames within | |
|
845 | that stream must use that content encoding. This can be useful when | |
|
846 | data is being served from caches and being derived dynamically. A | |
|
847 | cache could pre-compressed data so the server doesn't have to | |
|
848 | recompress it. The ability to pick and choose which frames are | |
|
849 | compressed allows servers to easily send data to the wire without | |
|
850 | involving potentially expensive encoding overhead. | |
|
851 | ||
|
852 | Content Encoding Profiles | |
|
853 | ------------------------- | |
|
854 | ||
|
855 | Streams can have named content encoding *profiles* associated with | |
|
856 | them. A profile defines a shared understanding of content encoding | |
|
857 | settings and behavior. | |
|
858 | ||
|
859 | The following profiles are defined: | |
|
860 | ||
|
861 | TBD | |
|
862 | ||
|
723 | 863 | Issuing Commands |
|
724 | 864 | ---------------- |
|
725 | 865 |
@@ -25,15 +25,26 b' from .utils import (' | |||
|
25 | 25 | stringutil, |
|
26 | 26 | ) |
|
27 | 27 | |
|
28 |
FRAME_HEADER_SIZE = |
|
|
28 | FRAME_HEADER_SIZE = 8 | |
|
29 | 29 | DEFAULT_MAX_FRAME_SIZE = 32768 |
|
30 | 30 | |
|
31 | STREAM_FLAG_BEGIN_STREAM = 0x01 | |
|
32 | STREAM_FLAG_END_STREAM = 0x02 | |
|
33 | STREAM_FLAG_ENCODING_APPLIED = 0x04 | |
|
34 | ||
|
35 | STREAM_FLAGS = { | |
|
36 | b'stream-begin': STREAM_FLAG_BEGIN_STREAM, | |
|
37 | b'stream-end': STREAM_FLAG_END_STREAM, | |
|
38 | b'encoded': STREAM_FLAG_ENCODING_APPLIED, | |
|
39 | } | |
|
40 | ||
|
31 | 41 | FRAME_TYPE_COMMAND_NAME = 0x01 |
|
32 | 42 | FRAME_TYPE_COMMAND_ARGUMENT = 0x02 |
|
33 | 43 | FRAME_TYPE_COMMAND_DATA = 0x03 |
|
34 | 44 | FRAME_TYPE_BYTES_RESPONSE = 0x04 |
|
35 | 45 | FRAME_TYPE_ERROR_RESPONSE = 0x05 |
|
36 | 46 | FRAME_TYPE_TEXT_OUTPUT = 0x06 |
|
47 | FRAME_TYPE_STREAM_SETTINGS = 0x08 | |
|
37 | 48 | |
|
38 | 49 | FRAME_TYPES = { |
|
39 | 50 | b'command-name': FRAME_TYPE_COMMAND_NAME, |
@@ -42,6 +53,7 b' FRAME_TYPES = {' | |||
|
42 | 53 | b'bytes-response': FRAME_TYPE_BYTES_RESPONSE, |
|
43 | 54 | b'error-response': FRAME_TYPE_ERROR_RESPONSE, |
|
44 | 55 | b'text-output': FRAME_TYPE_TEXT_OUTPUT, |
|
56 | b'stream-settings': FRAME_TYPE_STREAM_SETTINGS, | |
|
45 | 57 | } |
|
46 | 58 | |
|
47 | 59 | FLAG_COMMAND_NAME_EOS = 0x01 |
@@ -94,6 +106,7 b' FRAME_TYPE_FLAGS = {' | |||
|
94 | 106 | FRAME_TYPE_BYTES_RESPONSE: FLAGS_BYTES_RESPONSE, |
|
95 | 107 | FRAME_TYPE_ERROR_RESPONSE: FLAGS_ERROR_RESPONSE, |
|
96 | 108 | FRAME_TYPE_TEXT_OUTPUT: {}, |
|
109 | FRAME_TYPE_STREAM_SETTINGS: {}, | |
|
97 | 110 | } |
|
98 | 111 | |
|
99 | 112 | ARGUMENT_FRAME_HEADER = struct.Struct(r'<HH') |
@@ -104,6 +117,8 b' class frameheader(object):' | |||
|
104 | 117 | |
|
105 | 118 | length = attr.ib() |
|
106 | 119 | requestid = attr.ib() |
|
120 | streamid = attr.ib() | |
|
121 | streamflags = attr.ib() | |
|
107 | 122 | typeid = attr.ib() |
|
108 | 123 | flags = attr.ib() |
|
109 | 124 | |
@@ -112,25 +127,29 b' class frame(object):' | |||
|
112 | 127 | """Represents a parsed frame.""" |
|
113 | 128 | |
|
114 | 129 | requestid = attr.ib() |
|
130 | streamid = attr.ib() | |
|
131 | streamflags = attr.ib() | |
|
115 | 132 | typeid = attr.ib() |
|
116 | 133 | flags = attr.ib() |
|
117 | 134 | payload = attr.ib() |
|
118 | 135 | |
|
119 | def makeframe(requestid, typeid, flags, payload): | |
|
136 | def makeframe(requestid, streamid, streamflags, typeid, flags, payload): | |
|
120 | 137 | """Assemble a frame into a byte array.""" |
|
121 | 138 | # TODO assert size of payload. |
|
122 | 139 | frame = bytearray(FRAME_HEADER_SIZE + len(payload)) |
|
123 | 140 | |
|
124 | 141 | # 24 bits length |
|
125 | 142 | # 16 bits request id |
|
143 | # 8 bits stream id | |
|
144 | # 8 bits stream flags | |
|
126 | 145 | # 4 bits type |
|
127 | 146 | # 4 bits flags |
|
128 | 147 | |
|
129 | 148 | l = struct.pack(r'<I', len(payload)) |
|
130 | 149 | frame[0:3] = l[0:3] |
|
131 | struct.pack_into(r'<H', frame, 3, requestid) | |
|
132 |
frame[ |
|
|
133 |
frame[ |
|
|
150 | struct.pack_into(r'<HBB', frame, 3, requestid, streamid, streamflags) | |
|
151 | frame[7] = (typeid << 4) | flags | |
|
152 | frame[8:] = payload | |
|
134 | 153 | |
|
135 | 154 | return frame |
|
136 | 155 | |
@@ -139,20 +158,30 b' def makeframefromhumanstring(s):' | |||
|
139 | 158 | |
|
140 | 159 | Strings have the form: |
|
141 | 160 | |
|
142 | <request-id> <type> <flags> <payload> | |
|
161 | <request-id> <stream-id> <stream-flags> <type> <flags> <payload> | |
|
143 | 162 | |
|
144 | 163 | This can be used by user-facing applications and tests for creating |
|
145 | 164 | frames easily without having to type out a bunch of constants. |
|
146 | 165 | |
|
147 |
Request ID |
|
|
166 | Request ID and stream IDs are integers. | |
|
148 | 167 | |
|
149 |
|
|
|
168 | Stream flags, frame type, and flags can be specified by integer or | |
|
169 | named constant. | |
|
150 | 170 | |
|
151 | 171 | Flags can be delimited by `|` to bitwise OR them together. |
|
152 | 172 | """ |
|
153 | requestid, frametype, frameflags, payload = s.split(b' ', 3) | |
|
173 | fields = s.split(b' ', 5) | |
|
174 | requestid, streamid, streamflags, frametype, frameflags, payload = fields | |
|
154 | 175 | |
|
155 | 176 | requestid = int(requestid) |
|
177 | streamid = int(streamid) | |
|
178 | ||
|
179 | finalstreamflags = 0 | |
|
180 | for flag in streamflags.split(b'|'): | |
|
181 | if flag in STREAM_FLAGS: | |
|
182 | finalstreamflags |= STREAM_FLAGS[flag] | |
|
183 | else: | |
|
184 | finalstreamflags |= int(flag) | |
|
156 | 185 | |
|
157 | 186 | if frametype in FRAME_TYPES: |
|
158 | 187 | frametype = FRAME_TYPES[frametype] |
@@ -169,7 +198,8 b' def makeframefromhumanstring(s):' | |||
|
169 | 198 | |
|
170 | 199 | payload = stringutil.unescapestr(payload) |
|
171 | 200 | |
|
172 |
return makeframe(requestid=requestid, |
|
|
201 | return makeframe(requestid=requestid, streamid=streamid, | |
|
202 | streamflags=finalstreamflags, typeid=frametype, | |
|
173 | 203 | flags=finalflags, payload=payload) |
|
174 | 204 | |
|
175 | 205 | def parseheader(data): |
@@ -179,17 +209,21 b' def parseheader(data):' | |||
|
179 | 209 | buffer is expected to be large enough to hold a full header. |
|
180 | 210 | """ |
|
181 | 211 | # 24 bits payload length (little endian) |
|
212 | # 16 bits request ID | |
|
213 | # 8 bits stream ID | |
|
214 | # 8 bits stream flags | |
|
182 | 215 | # 4 bits frame type |
|
183 | 216 | # 4 bits frame flags |
|
184 | 217 | # ... payload |
|
185 | 218 | framelength = data[0] + 256 * data[1] + 16384 * data[2] |
|
186 |
requestid = struct.unpack_from(r'<H', data, 3) |
|
|
187 |
typeflags = data[ |
|
|
219 | requestid, streamid, streamflags = struct.unpack_from(r'<HBB', data, 3) | |
|
220 | typeflags = data[7] | |
|
188 | 221 | |
|
189 | 222 | frametype = (typeflags & 0xf0) >> 4 |
|
190 | 223 | frameflags = typeflags & 0x0f |
|
191 | 224 | |
|
192 |
return frameheader(framelength, requestid, |
|
|
225 | return frameheader(framelength, requestid, streamid, streamflags, | |
|
226 | frametype, frameflags) | |
|
193 | 227 | |
|
194 | 228 | def readframe(fh): |
|
195 | 229 | """Read a unified framing protocol frame from a file object. |
@@ -216,7 +250,8 b' def readframe(fh):' | |||
|
216 | 250 | raise error.Abort(_('frame length error: expected %d; got %d') % |
|
217 | 251 | (h.length, len(payload))) |
|
218 | 252 | |
|
219 |
return frame(h.requestid, h.typeid, h.flags, |
|
|
253 | return frame(h.requestid, h.streamid, h.streamflags, h.typeid, h.flags, | |
|
254 | payload) | |
|
220 | 255 | |
|
221 | 256 | def createcommandframes(stream, requestid, cmd, args, datafh=None): |
|
222 | 257 | """Create frames necessary to transmit a request to run a command. |
@@ -398,12 +433,28 b' def createtextoutputframe(stream, reques' | |||
|
398 | 433 | class stream(object): |
|
399 | 434 | """Represents a logical unidirectional series of frames.""" |
|
400 | 435 | |
|
436 | def __init__(self, streamid, active=False): | |
|
437 | self.streamid = streamid | |
|
438 | self._active = False | |
|
439 | ||
|
401 | 440 | def makeframe(self, requestid, typeid, flags, payload): |
|
402 | 441 | """Create a frame to be sent out over this stream. |
|
403 | 442 | |
|
404 | 443 | Only returns the frame instance. Does not actually send it. |
|
405 | 444 | """ |
|
406 | return makeframe(requestid, typeid, flags, payload) | |
|
445 | streamflags = 0 | |
|
446 | if not self._active: | |
|
447 | streamflags |= STREAM_FLAG_BEGIN_STREAM | |
|
448 | self._active = True | |
|
449 | ||
|
450 | return makeframe(requestid, self.streamid, streamflags, typeid, flags, | |
|
451 | payload) | |
|
452 | ||
|
453 | def ensureserverstream(stream): | |
|
454 | if stream.streamid % 2: | |
|
455 | raise error.ProgrammingError('server should only write to even ' | |
|
456 | 'numbered streams; %d is not even' % | |
|
457 | stream.streamid) | |
|
407 | 458 | |
|
408 | 459 | class serverreactor(object): |
|
409 | 460 | """Holds state of a server handling frame-based protocol requests. |
@@ -483,6 +534,8 b' class serverreactor(object):' | |||
|
483 | 534 | self._deferoutput = deferoutput |
|
484 | 535 | self._state = 'idle' |
|
485 | 536 | self._bufferedframegens = [] |
|
537 | # stream id -> stream instance for all active streams from the client. | |
|
538 | self._incomingstreams = {} | |
|
486 | 539 | # request id -> dict of commands that are actively being received. |
|
487 | 540 | self._receivingcommands = {} |
|
488 | 541 | # Request IDs that have been received and are actively being processed. |
@@ -496,6 +549,30 b' class serverreactor(object):' | |||
|
496 | 549 | Returns a dict with an ``action`` key that details what action, |
|
497 | 550 | if any, the consumer should take next. |
|
498 | 551 | """ |
|
552 | if not frame.streamid % 2: | |
|
553 | self._state = 'errored' | |
|
554 | return self._makeerrorresult( | |
|
555 | _('received frame with even numbered stream ID: %d') % | |
|
556 | frame.streamid) | |
|
557 | ||
|
558 | if frame.streamid not in self._incomingstreams: | |
|
559 | if not frame.streamflags & STREAM_FLAG_BEGIN_STREAM: | |
|
560 | self._state = 'errored' | |
|
561 | return self._makeerrorresult( | |
|
562 | _('received frame on unknown inactive stream without ' | |
|
563 | 'beginning of stream flag set')) | |
|
564 | ||
|
565 | self._incomingstreams[frame.streamid] = stream(frame.streamid) | |
|
566 | ||
|
567 | if frame.streamflags & STREAM_FLAG_ENCODING_APPLIED: | |
|
568 | # TODO handle decoding frames | |
|
569 | self._state = 'errored' | |
|
570 | raise error.ProgrammingError('support for decoding stream payloads ' | |
|
571 | 'not yet implemented') | |
|
572 | ||
|
573 | if frame.streamflags & STREAM_FLAG_END_STREAM: | |
|
574 | del self._incomingstreams[frame.streamid] | |
|
575 | ||
|
499 | 576 | handlers = { |
|
500 | 577 | 'idle': self._onframeidle, |
|
501 | 578 | 'command-receiving': self._onframecommandreceiving, |
@@ -513,6 +590,8 b' class serverreactor(object):' | |||
|
513 | 590 | |
|
514 | 591 | The raw bytes response is passed as an argument. |
|
515 | 592 | """ |
|
593 | ensureserverstream(stream) | |
|
594 | ||
|
516 | 595 | def sendframes(): |
|
517 | 596 | for frame in createbytesresponseframesfrombytes(stream, requestid, |
|
518 | 597 | data): |
@@ -552,6 +631,8 b' class serverreactor(object):' | |||
|
552 | 631 | } |
|
553 | 632 | |
|
554 | 633 | def onapplicationerror(self, stream, requestid, msg): |
|
634 | ensureserverstream(stream) | |
|
635 | ||
|
555 | 636 | return 'sendframes', { |
|
556 | 637 | 'framegen': createerrorframe(stream, requestid, msg, |
|
557 | 638 | application=True), |
@@ -546,7 +546,7 b' def _httpv2runcommand(ui, repo, req, res' | |||
|
546 | 546 | |
|
547 | 547 | res.status = b'200 OK' |
|
548 | 548 | res.headers[b'Content-Type'] = FRAMINGTYPE |
|
549 | stream = wireprotoframing.stream() | |
|
549 | stream = wireprotoframing.stream(2) | |
|
550 | 550 | |
|
551 | 551 | if isinstance(rsp, wireprototypes.bytesresponse): |
|
552 | 552 | action, meta = reactor.onbytesresponseready(stream, |
@@ -179,7 +179,7 b' Request to read-only command works out o' | |||
|
179 | 179 | > accept: $MEDIATYPE |
|
180 | 180 | > content-type: $MEDIATYPE |
|
181 | 181 | > user-agent: test |
|
182 | > frame 1 command-name eos customreadonly | |
|
182 | > frame 1 1 stream-begin command-name eos customreadonly | |
|
183 | 183 | > EOF |
|
184 | 184 | using raw connection to peer |
|
185 | 185 | s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n |
@@ -190,7 +190,7 b' Request to read-only command works out o' | |||
|
190 | 190 | s> *\r\n (glob) |
|
191 | 191 | s> host: $LOCALIP:$HGPORT\r\n (glob) |
|
192 | 192 | s> \r\n |
|
193 | s> \x0e\x00\x00\x01\x00\x11customreadonly | |
|
193 | s> \x0e\x00\x00\x01\x00\x01\x01\x11customreadonly | |
|
194 | 194 | s> makefile('rb', None) |
|
195 | 195 | s> HTTP/1.1 200 OK\r\n |
|
196 | 196 | s> Server: testing stub value\r\n |
@@ -198,8 +198,8 b' Request to read-only command works out o' | |||
|
198 | 198 | s> Content-Type: application/mercurial-exp-framing-0002\r\n |
|
199 | 199 | s> Transfer-Encoding: chunked\r\n |
|
200 | 200 | s> \r\n |
|
201 |
s> 2 |
|
|
202 | s> \x1d\x00\x00\x01\x00Bcustomreadonly bytes response | |
|
201 | s> 25\r\n | |
|
202 | s> \x1d\x00\x00\x01\x00\x02\x01Bcustomreadonly bytes response | |
|
203 | 203 | s> \r\n |
|
204 | 204 | s> 0\r\n |
|
205 | 205 | s> \r\n |
@@ -290,7 +290,7 b' Authorized request for valid read-write ' | |||
|
290 | 290 | > user-agent: test |
|
291 | 291 | > accept: $MEDIATYPE |
|
292 | 292 | > content-type: $MEDIATYPE |
|
293 | > frame 1 command-name eos customreadonly | |
|
293 | > frame 1 1 stream-begin command-name eos customreadonly | |
|
294 | 294 | > EOF |
|
295 | 295 | using raw connection to peer |
|
296 | 296 | s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n |
@@ -298,10 +298,10 b' Authorized request for valid read-write ' | |||
|
298 | 298 | s> accept: application/mercurial-exp-framing-0002\r\n |
|
299 | 299 | s> content-type: application/mercurial-exp-framing-0002\r\n |
|
300 | 300 | s> user-agent: test\r\n |
|
301 |
s> content-length: 2 |
|
|
301 | s> content-length: 22\r\n | |
|
302 | 302 | s> host: $LOCALIP:$HGPORT\r\n (glob) |
|
303 | 303 | s> \r\n |
|
304 | s> \x0e\x00\x00\x01\x00\x11customreadonly | |
|
304 | s> \x0e\x00\x00\x01\x00\x01\x01\x11customreadonly | |
|
305 | 305 | s> makefile('rb', None) |
|
306 | 306 | s> HTTP/1.1 200 OK\r\n |
|
307 | 307 | s> Server: testing stub value\r\n |
@@ -309,8 +309,8 b' Authorized request for valid read-write ' | |||
|
309 | 309 | s> Content-Type: application/mercurial-exp-framing-0002\r\n |
|
310 | 310 | s> Transfer-Encoding: chunked\r\n |
|
311 | 311 | s> \r\n |
|
312 |
s> 2 |
|
|
313 | s> \x1d\x00\x00\x01\x00Bcustomreadonly bytes response | |
|
312 | s> 25\r\n | |
|
313 | s> \x1d\x00\x00\x01\x00\x02\x01Bcustomreadonly bytes response | |
|
314 | 314 | s> \r\n |
|
315 | 315 | s> 0\r\n |
|
316 | 316 | s> \r\n |
@@ -382,9 +382,9 b' Command frames can be reflected via debu' | |||
|
382 | 382 | > accept: $MEDIATYPE |
|
383 | 383 | > content-type: $MEDIATYPE |
|
384 | 384 | > user-agent: test |
|
385 | > frame 1 command-name have-args command1 | |
|
386 | > frame 1 command-argument 0 \x03\x00\x04\x00fooval1 | |
|
387 | > frame 1 command-argument eoa \x04\x00\x03\x00bar1val | |
|
385 | > frame 1 1 stream-begin command-name have-args command1 | |
|
386 | > frame 1 1 0 command-argument 0 \x03\x00\x04\x00fooval1 | |
|
387 | > frame 1 1 0 command-argument eoa \x04\x00\x03\x00bar1val | |
|
388 | 388 | > EOF |
|
389 | 389 | using raw connection to peer |
|
390 | 390 | s> POST /api/exp-http-v2-0001/ro/debugreflect HTTP/1.1\r\n |
@@ -392,10 +392,10 b' Command frames can be reflected via debu' | |||
|
392 | 392 | s> accept: application/mercurial-exp-framing-0002\r\n |
|
393 | 393 | s> content-type: application/mercurial-exp-framing-0002\r\n |
|
394 | 394 | s> user-agent: test\r\n |
|
395 |
s> content-length: 4 |
|
|
395 | s> content-length: 54\r\n | |
|
396 | 396 | s> host: $LOCALIP:$HGPORT\r\n (glob) |
|
397 | 397 | s> \r\n |
|
398 | s> \x08\x00\x00\x01\x00\x12command1\x0b\x00\x00\x01\x00 \x03\x00\x04\x00fooval1\x0b\x00\x00\x01\x00"\x04\x00\x03\x00bar1val | |
|
398 | s> \x08\x00\x00\x01\x00\x01\x01\x12command1\x0b\x00\x00\x01\x00\x01\x00 \x03\x00\x04\x00fooval1\x0b\x00\x00\x01\x00\x01\x00"\x04\x00\x03\x00bar1val | |
|
399 | 399 | s> makefile('rb', None) |
|
400 | 400 | s> HTTP/1.1 200 OK\r\n |
|
401 | 401 | s> Server: testing stub value\r\n |
@@ -419,8 +419,8 b' Multiple requests to regular command URL' | |||
|
419 | 419 | > accept: $MEDIATYPE |
|
420 | 420 | > content-type: $MEDIATYPE |
|
421 | 421 | > user-agent: test |
|
422 | > frame 1 command-name eos customreadonly | |
|
423 | > frame 3 command-name eos customreadonly | |
|
422 | > frame 1 1 stream-begin command-name eos customreadonly | |
|
423 | > frame 3 1 0 command-name eos customreadonly | |
|
424 | 424 | > EOF |
|
425 | 425 | using raw connection to peer |
|
426 | 426 | s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n |
@@ -428,10 +428,10 b' Multiple requests to regular command URL' | |||
|
428 | 428 | s> accept: application/mercurial-exp-framing-0002\r\n |
|
429 | 429 | s> content-type: application/mercurial-exp-framing-0002\r\n |
|
430 | 430 | s> user-agent: test\r\n |
|
431 |
s> content-length: 4 |
|
|
431 | s> content-length: 44\r\n | |
|
432 | 432 | s> host: $LOCALIP:$HGPORT\r\n (glob) |
|
433 | 433 | s> \r\n |
|
434 | s> \x0e\x00\x00\x01\x00\x11customreadonly\x0e\x00\x00\x03\x00\x11customreadonly | |
|
434 | s> \x0e\x00\x00\x01\x00\x01\x01\x11customreadonly\x0e\x00\x00\x03\x00\x01\x00\x11customreadonly | |
|
435 | 435 | s> makefile('rb', None) |
|
436 | 436 | s> HTTP/1.1 200 OK\r\n |
|
437 | 437 | s> Server: testing stub value\r\n |
@@ -448,8 +448,8 b' Multiple requests to "multirequest" URL ' | |||
|
448 | 448 | > accept: $MEDIATYPE |
|
449 | 449 | > content-type: $MEDIATYPE |
|
450 | 450 | > user-agent: test |
|
451 | > frame 1 command-name eos customreadonly | |
|
452 | > frame 3 command-name eos customreadonly | |
|
451 | > frame 1 1 stream-begin command-name eos customreadonly | |
|
452 | > frame 3 1 0 command-name eos customreadonly | |
|
453 | 453 | > EOF |
|
454 | 454 | using raw connection to peer |
|
455 | 455 | s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n |
@@ -460,7 +460,7 b' Multiple requests to "multirequest" URL ' | |||
|
460 | 460 | s> *\r\n (glob) |
|
461 | 461 | s> host: $LOCALIP:$HGPORT\r\n (glob) |
|
462 | 462 | s> \r\n |
|
463 | s> \x0e\x00\x00\x01\x00\x11customreadonly\x0e\x00\x00\x03\x00\x11customreadonly | |
|
463 | s> \x0e\x00\x00\x01\x00\x01\x01\x11customreadonly\x0e\x00\x00\x03\x00\x01\x00\x11customreadonly | |
|
464 | 464 | s> makefile('rb', None) |
|
465 | 465 | s> HTTP/1.1 200 OK\r\n |
|
466 | 466 | s> Server: testing stub value\r\n |
@@ -469,10 +469,10 b' Multiple requests to "multirequest" URL ' | |||
|
469 | 469 | s> Transfer-Encoding: chunked\r\n |
|
470 | 470 | s> \r\n |
|
471 | 471 | s> *\r\n (glob) |
|
472 | s> \x1d\x00\x00\x01\x00Bcustomreadonly bytes response | |
|
472 | s> \x1d\x00\x00\x01\x00\x02\x01Bcustomreadonly bytes response | |
|
473 | 473 | s> \r\n |
|
474 |
s> 2 |
|
|
475 | s> \x1d\x00\x00\x03\x00Bcustomreadonly bytes response | |
|
474 | s> 25\r\n | |
|
475 | s> \x1d\x00\x00\x03\x00\x02\x01Bcustomreadonly bytes response | |
|
476 | 476 | s> \r\n |
|
477 | 477 | s> 0\r\n |
|
478 | 478 | s> \r\n |
@@ -484,10 +484,10 b' Interleaved requests to "multirequest" a' | |||
|
484 | 484 | > accept: $MEDIATYPE |
|
485 | 485 | > content-type: $MEDIATYPE |
|
486 | 486 | > user-agent: test |
|
487 | > frame 1 command-name have-args listkeys | |
|
488 | > frame 3 command-name have-args listkeys | |
|
489 | > frame 3 command-argument eoa \x09\x00\x09\x00namespacebookmarks | |
|
490 | > frame 1 command-argument eoa \x09\x00\x0a\x00namespacenamespaces | |
|
487 | > frame 1 1 stream-begin command-name have-args listkeys | |
|
488 | > frame 3 1 0 command-name have-args listkeys | |
|
489 | > frame 3 1 0 command-argument eoa \x09\x00\x09\x00namespacebookmarks | |
|
490 | > frame 1 1 0 command-argument eoa \x09\x00\x0a\x00namespacenamespaces | |
|
491 | 491 | > EOF |
|
492 | 492 | using raw connection to peer |
|
493 | 493 | s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n |
@@ -495,10 +495,10 b' Interleaved requests to "multirequest" a' | |||
|
495 | 495 | s> accept: application/mercurial-exp-framing-0002\r\n |
|
496 | 496 | s> content-type: application/mercurial-exp-framing-0002\r\n |
|
497 | 497 | s> user-agent: test\r\n |
|
498 |
s> content-length: |
|
|
498 | s> content-length: 93\r\n | |
|
499 | 499 | s> host: $LOCALIP:$HGPORT\r\n (glob) |
|
500 | 500 | s> \r\n |
|
501 | s> \x08\x00\x00\x01\x00\x12listkeys\x08\x00\x00\x03\x00\x12listkeys\x16\x00\x00\x03\x00" \x00 \x00namespacebookmarks\x17\x00\x00\x01\x00" \x00\n | |
|
501 | s> \x08\x00\x00\x01\x00\x01\x01\x12listkeys\x08\x00\x00\x03\x00\x01\x00\x12listkeys\x16\x00\x00\x03\x00\x01\x00" \x00 \x00namespacebookmarks\x17\x00\x00\x01\x00\x01\x00" \x00\n | |
|
502 | 502 | s> \x00namespacenamespaces |
|
503 | 503 | s> makefile('rb', None) |
|
504 | 504 | s> HTTP/1.1 200 OK\r\n |
@@ -507,11 +507,11 b' Interleaved requests to "multirequest" a' | |||
|
507 | 507 | s> Content-Type: application/mercurial-exp-framing-0002\r\n |
|
508 | 508 | s> Transfer-Encoding: chunked\r\n |
|
509 | 509 | s> \r\n |
|
510 |
s> |
|
|
511 |
s> \x00\x00\x00\x03\x00 |
|
|
510 | s> 8\r\n | |
|
511 | s> \x00\x00\x00\x03\x00\x02\x01B | |
|
512 | 512 | s> \r\n |
|
513 |
s> 2 |
|
|
514 | s> \x1e\x00\x00\x01\x00Bbookmarks \n | |
|
513 | s> 26\r\n | |
|
514 | s> \x1e\x00\x00\x01\x00\x02\x01Bbookmarks \n | |
|
515 | 515 | s> namespaces \n |
|
516 | 516 | s> phases |
|
517 | 517 | s> \r\n |
@@ -540,7 +540,7 b' Attempting to run a read-write command v' | |||
|
540 | 540 | > accept: $MEDIATYPE |
|
541 | 541 | > content-type: $MEDIATYPE |
|
542 | 542 | > user-agent: test |
|
543 | > frame 1 command-name eos unbundle | |
|
543 | > frame 1 1 stream-begin command-name eos unbundle | |
|
544 | 544 | > EOF |
|
545 | 545 | using raw connection to peer |
|
546 | 546 | s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n |
@@ -548,10 +548,10 b' Attempting to run a read-write command v' | |||
|
548 | 548 | s> accept: application/mercurial-exp-framing-0002\r\n |
|
549 | 549 | s> content-type: application/mercurial-exp-framing-0002\r\n |
|
550 | 550 | s> user-agent: test\r\n |
|
551 |
s> content-length: 1 |
|
|
551 | s> content-length: 16\r\n | |
|
552 | 552 | s> host: $LOCALIP:$HGPORT\r\n (glob) |
|
553 | 553 | s> \r\n |
|
554 | s> \x08\x00\x00\x01\x00\x11unbundle | |
|
554 | s> \x08\x00\x00\x01\x00\x01\x01\x11unbundle | |
|
555 | 555 | s> makefile('rb', None) |
|
556 | 556 | s> HTTP/1.1 403 Forbidden\r\n |
|
557 | 557 | s> Server: testing stub value\r\n |
@@ -23,6 +23,8 b' def sendframes(reactor, gen):' | |||
|
23 | 23 | assert len(payload) == header.length |
|
24 | 24 | |
|
25 | 25 | yield reactor.onframerecv(framing.frame(header.requestid, |
|
26 | header.streamid, | |
|
27 | header.streamflags, | |
|
26 | 28 | header.typeid, |
|
27 | 29 | header.flags, |
|
28 | 30 | payload)) |
@@ -37,32 +39,32 b' class FrameTests(unittest.TestCase):' | |||
|
37 | 39 | def testdataexactframesize(self): |
|
38 | 40 | data = util.bytesio(b'x' * framing.DEFAULT_MAX_FRAME_SIZE) |
|
39 | 41 | |
|
40 | stream = framing.stream() | |
|
42 | stream = framing.stream(1) | |
|
41 | 43 | frames = list(framing.createcommandframes(stream, 1, b'command', |
|
42 | 44 | {}, data)) |
|
43 | 45 | self.assertEqual(frames, [ |
|
44 | ffs(b'1 command-name have-data command'), | |
|
45 | ffs(b'1 command-data continuation %s' % data.getvalue()), | |
|
46 | ffs(b'1 command-data eos ') | |
|
46 | ffs(b'1 1 stream-begin command-name have-data command'), | |
|
47 | ffs(b'1 1 0 command-data continuation %s' % data.getvalue()), | |
|
48 | ffs(b'1 1 0 command-data eos ') | |
|
47 | 49 | ]) |
|
48 | 50 | |
|
49 | 51 | def testdatamultipleframes(self): |
|
50 | 52 | data = util.bytesio(b'x' * (framing.DEFAULT_MAX_FRAME_SIZE + 1)) |
|
51 | 53 | |
|
52 | stream = framing.stream() | |
|
54 | stream = framing.stream(1) | |
|
53 | 55 | frames = list(framing.createcommandframes(stream, 1, b'command', {}, |
|
54 | 56 | data)) |
|
55 | 57 | self.assertEqual(frames, [ |
|
56 | ffs(b'1 command-name have-data command'), | |
|
57 | ffs(b'1 command-data continuation %s' % ( | |
|
58 | ffs(b'1 1 stream-begin command-name have-data command'), | |
|
59 | ffs(b'1 1 0 command-data continuation %s' % ( | |
|
58 | 60 | b'x' * framing.DEFAULT_MAX_FRAME_SIZE)), |
|
59 | ffs(b'1 command-data eos x'), | |
|
61 | ffs(b'1 1 0 command-data eos x'), | |
|
60 | 62 | ]) |
|
61 | 63 | |
|
62 | 64 | def testargsanddata(self): |
|
63 | 65 | data = util.bytesio(b'x' * 100) |
|
64 | 66 | |
|
65 | stream = framing.stream() | |
|
67 | stream = framing.stream(1) | |
|
66 | 68 | frames = list(framing.createcommandframes(stream, 1, b'command', { |
|
67 | 69 | b'key1': b'key1value', |
|
68 | 70 | b'key2': b'key2value', |
@@ -70,11 +72,11 b' class FrameTests(unittest.TestCase):' | |||
|
70 | 72 | }, data)) |
|
71 | 73 | |
|
72 | 74 | self.assertEqual(frames, [ |
|
73 | ffs(b'1 command-name have-args|have-data command'), | |
|
74 | ffs(br'1 command-argument 0 \x04\x00\x09\x00key1key1value'), | |
|
75 | ffs(br'1 command-argument 0 \x04\x00\x09\x00key2key2value'), | |
|
76 | ffs(br'1 command-argument eoa \x04\x00\x09\x00key3key3value'), | |
|
77 | ffs(b'1 command-data eos %s' % data.getvalue()), | |
|
75 | ffs(b'1 1 stream-begin command-name have-args|have-data command'), | |
|
76 | ffs(br'1 1 0 command-argument 0 \x04\x00\x09\x00key1key1value'), | |
|
77 | ffs(br'1 1 0 command-argument 0 \x04\x00\x09\x00key2key2value'), | |
|
78 | ffs(br'1 1 0 command-argument eoa \x04\x00\x09\x00key3key3value'), | |
|
79 | ffs(b'1 1 0 command-data eos %s' % data.getvalue()), | |
|
78 | 80 | ]) |
|
79 | 81 | |
|
80 | 82 | def testtextoutputexcessiveargs(self): |
@@ -128,64 +130,68 b' class FrameTests(unittest.TestCase):' | |||
|
128 | 130 | (b'bleh', [], [b'x' * 65536])])) |
|
129 | 131 | |
|
130 | 132 | def testtextoutput1simpleatom(self): |
|
131 | stream = framing.stream() | |
|
133 | stream = framing.stream(1) | |
|
132 | 134 | val = list(framing.createtextoutputframe(stream, 1, [ |
|
133 | 135 | (b'foo', [], [])])) |
|
134 | 136 | |
|
135 | 137 | self.assertEqual(val, [ |
|
136 | ffs(br'1 text-output 0 \x03\x00\x00\x00foo'), | |
|
138 | ffs(br'1 1 stream-begin text-output 0 \x03\x00\x00\x00foo'), | |
|
137 | 139 | ]) |
|
138 | 140 | |
|
139 | 141 | def testtextoutput2simpleatoms(self): |
|
140 | stream = framing.stream() | |
|
142 | stream = framing.stream(1) | |
|
141 | 143 | val = list(framing.createtextoutputframe(stream, 1, [ |
|
142 | 144 | (b'foo', [], []), |
|
143 | 145 | (b'bar', [], []), |
|
144 | 146 | ])) |
|
145 | 147 | |
|
146 | 148 | self.assertEqual(val, [ |
|
147 | ffs(br'1 text-output 0 \x03\x00\x00\x00foo\x03\x00\x00\x00bar'), | |
|
149 | ffs(br'1 1 stream-begin text-output 0 ' | |
|
150 | br'\x03\x00\x00\x00foo\x03\x00\x00\x00bar'), | |
|
148 | 151 | ]) |
|
149 | 152 | |
|
150 | 153 | def testtextoutput1arg(self): |
|
151 | stream = framing.stream() | |
|
154 | stream = framing.stream(1) | |
|
152 | 155 | val = list(framing.createtextoutputframe(stream, 1, [ |
|
153 | 156 | (b'foo %s', [b'val1'], []), |
|
154 | 157 | ])) |
|
155 | 158 | |
|
156 | 159 | self.assertEqual(val, [ |
|
157 | ffs(br'1 text-output 0 \x06\x00\x00\x01\x04\x00foo %sval1'), | |
|
160 | ffs(br'1 1 stream-begin text-output 0 ' | |
|
161 | br'\x06\x00\x00\x01\x04\x00foo %sval1'), | |
|
158 | 162 | ]) |
|
159 | 163 | |
|
160 | 164 | def testtextoutput2arg(self): |
|
161 | stream = framing.stream() | |
|
165 | stream = framing.stream(1) | |
|
162 | 166 | val = list(framing.createtextoutputframe(stream, 1, [ |
|
163 | 167 | (b'foo %s %s', [b'val', b'value'], []), |
|
164 | 168 | ])) |
|
165 | 169 | |
|
166 | 170 | self.assertEqual(val, [ |
|
167 |
ffs(br'1 text-output 0 |
|
|
168 | br'foo %s %svalvalue'), | |
|
171 | ffs(br'1 1 stream-begin text-output 0 ' | |
|
172 | br'\x09\x00\x00\x02\x03\x00\x05\x00foo %s %svalvalue'), | |
|
169 | 173 | ]) |
|
170 | 174 | |
|
171 | 175 | def testtextoutput1label(self): |
|
172 | stream = framing.stream() | |
|
176 | stream = framing.stream(1) | |
|
173 | 177 | val = list(framing.createtextoutputframe(stream, 1, [ |
|
174 | 178 | (b'foo', [], [b'label']), |
|
175 | 179 | ])) |
|
176 | 180 | |
|
177 | 181 | self.assertEqual(val, [ |
|
178 |
ffs(br'1 text-output 0 |
|
|
182 | ffs(br'1 1 stream-begin text-output 0 ' | |
|
183 | br'\x03\x00\x01\x00\x05foolabel'), | |
|
179 | 184 | ]) |
|
180 | 185 | |
|
181 | 186 | def testargandlabel(self): |
|
182 | stream = framing.stream() | |
|
187 | stream = framing.stream(1) | |
|
183 | 188 | val = list(framing.createtextoutputframe(stream, 1, [ |
|
184 | 189 | (b'foo %s', [b'arg'], [b'label']), |
|
185 | 190 | ])) |
|
186 | 191 | |
|
187 | 192 | self.assertEqual(val, [ |
|
188 | ffs(br'1 text-output 0 \x06\x00\x01\x01\x05\x03\x00foo %slabelarg'), | |
|
193 | ffs(br'1 1 stream-begin text-output 0 ' | |
|
194 | br'\x06\x00\x01\x01\x05\x03\x00foo %slabelarg'), | |
|
189 | 195 | ]) |
|
190 | 196 | |
|
191 | 197 | class ServerReactorTests(unittest.TestCase): |
@@ -208,7 +214,7 b' class ServerReactorTests(unittest.TestCa' | |||
|
208 | 214 | def test1framecommand(self): |
|
209 | 215 | """Receiving a command in a single frame yields request to run it.""" |
|
210 | 216 | reactor = makereactor() |
|
211 | stream = framing.stream() | |
|
217 | stream = framing.stream(1) | |
|
212 | 218 | results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {})) |
|
213 | 219 | self.assertEqual(len(results), 1) |
|
214 | 220 | self.assertaction(results[0], 'runcommand') |
@@ -224,7 +230,7 b' class ServerReactorTests(unittest.TestCa' | |||
|
224 | 230 | |
|
225 | 231 | def test1argument(self): |
|
226 | 232 | reactor = makereactor() |
|
227 | stream = framing.stream() | |
|
233 | stream = framing.stream(1) | |
|
228 | 234 | results = list(sendcommandframes(reactor, stream, 41, b'mycommand', |
|
229 | 235 | {b'foo': b'bar'})) |
|
230 | 236 | self.assertEqual(len(results), 2) |
@@ -239,7 +245,7 b' class ServerReactorTests(unittest.TestCa' | |||
|
239 | 245 | |
|
240 | 246 | def testmultiarguments(self): |
|
241 | 247 | reactor = makereactor() |
|
242 | stream = framing.stream() | |
|
248 | stream = framing.stream(1) | |
|
243 | 249 | results = list(sendcommandframes(reactor, stream, 1, b'mycommand', |
|
244 | 250 | {b'foo': b'bar', b'biz': b'baz'})) |
|
245 | 251 | self.assertEqual(len(results), 3) |
@@ -255,7 +261,7 b' class ServerReactorTests(unittest.TestCa' | |||
|
255 | 261 | |
|
256 | 262 | def testsimplecommanddata(self): |
|
257 | 263 | reactor = makereactor() |
|
258 | stream = framing.stream() | |
|
264 | stream = framing.stream(1) | |
|
259 | 265 | results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}, |
|
260 | 266 | util.bytesio(b'data!'))) |
|
261 | 267 | self.assertEqual(len(results), 2) |
@@ -270,10 +276,10 b' class ServerReactorTests(unittest.TestCa' | |||
|
270 | 276 | |
|
271 | 277 | def testmultipledataframes(self): |
|
272 | 278 | frames = [ |
|
273 | ffs(b'1 command-name have-data mycommand'), | |
|
274 | ffs(b'1 command-data continuation data1'), | |
|
275 | ffs(b'1 command-data continuation data2'), | |
|
276 | ffs(b'1 command-data eos data3'), | |
|
279 | ffs(b'1 1 stream-begin command-name have-data mycommand'), | |
|
280 | ffs(b'1 1 0 command-data continuation data1'), | |
|
281 | ffs(b'1 1 0 command-data continuation data2'), | |
|
282 | ffs(b'1 1 0 command-data eos data3'), | |
|
277 | 283 | ] |
|
278 | 284 | |
|
279 | 285 | reactor = makereactor() |
@@ -291,11 +297,11 b' class ServerReactorTests(unittest.TestCa' | |||
|
291 | 297 | |
|
292 | 298 | def testargumentanddata(self): |
|
293 | 299 | frames = [ |
|
294 | ffs(b'1 command-name have-args|have-data command'), | |
|
295 | ffs(br'1 command-argument 0 \x03\x00\x03\x00keyval'), | |
|
296 | ffs(br'1 command-argument eoa \x03\x00\x03\x00foobar'), | |
|
297 | ffs(b'1 command-data continuation value1'), | |
|
298 | ffs(b'1 command-data eos value2'), | |
|
300 | ffs(b'1 1 stream-begin command-name have-args|have-data command'), | |
|
301 | ffs(br'1 1 0 command-argument 0 \x03\x00\x03\x00keyval'), | |
|
302 | ffs(br'1 1 0 command-argument eoa \x03\x00\x03\x00foobar'), | |
|
303 | ffs(b'1 1 0 command-data continuation value1'), | |
|
304 | ffs(b'1 1 0 command-data eos value2'), | |
|
299 | 305 | ] |
|
300 | 306 | |
|
301 | 307 | reactor = makereactor() |
@@ -314,8 +320,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
314 | 320 | |
|
315 | 321 | def testunexpectedcommandargument(self): |
|
316 | 322 | """Command argument frame when not running a command is an error.""" |
|
317 |
result = self._sendsingleframe( |
|
|
318 |
|
|
|
323 | result = self._sendsingleframe( | |
|
324 | makereactor(), ffs(b'1 1 stream-begin command-argument 0 ignored')) | |
|
319 | 325 | self.assertaction(result, 'error') |
|
320 | 326 | self.assertEqual(result[1], { |
|
321 | 327 | 'message': b'expected command frame; got 2', |
@@ -324,8 +330,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
324 | 330 | def testunexpectedcommandargumentreceiving(self): |
|
325 | 331 | """Same as above but the command is receiving.""" |
|
326 | 332 | results = list(sendframes(makereactor(), [ |
|
327 | ffs(b'1 command-name have-data command'), | |
|
328 | ffs(b'1 command-argument eoa ignored'), | |
|
333 | ffs(b'1 1 stream-begin command-name have-data command'), | |
|
334 | ffs(b'1 1 0 command-argument eoa ignored'), | |
|
329 | 335 | ])) |
|
330 | 336 | |
|
331 | 337 | self.assertaction(results[1], 'error') |
@@ -336,8 +342,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
336 | 342 | |
|
337 | 343 | def testunexpectedcommanddata(self): |
|
338 | 344 | """Command argument frame when not running a command is an error.""" |
|
339 |
result = self._sendsingleframe( |
|
|
340 |
|
|
|
345 | result = self._sendsingleframe( | |
|
346 | makereactor(), ffs(b'1 1 stream-begin command-data 0 ignored')) | |
|
341 | 347 | self.assertaction(result, 'error') |
|
342 | 348 | self.assertEqual(result[1], { |
|
343 | 349 | 'message': b'expected command frame; got 3', |
@@ -346,8 +352,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
346 | 352 | def testunexpectedcommanddatareceiving(self): |
|
347 | 353 | """Same as above except the command is receiving.""" |
|
348 | 354 | results = list(sendframes(makereactor(), [ |
|
349 | ffs(b'1 command-name have-args command'), | |
|
350 | ffs(b'1 command-data eos ignored'), | |
|
355 | ffs(b'1 1 stream-begin command-name have-args command'), | |
|
356 | ffs(b'1 1 0 command-data eos ignored'), | |
|
351 | 357 | ])) |
|
352 | 358 | |
|
353 | 359 | self.assertaction(results[1], 'error') |
@@ -358,8 +364,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
358 | 364 | |
|
359 | 365 | def testmissingcommandframeflags(self): |
|
360 | 366 | """Command name frame must have flags set.""" |
|
361 |
result = self._sendsingleframe( |
|
|
362 |
|
|
|
367 | result = self._sendsingleframe( | |
|
368 | makereactor(), ffs(b'1 1 stream-begin command-name 0 command')) | |
|
363 | 369 | self.assertaction(result, 'error') |
|
364 | 370 | self.assertEqual(result[1], { |
|
365 | 371 | 'message': b'missing frame flags on command frame', |
@@ -369,19 +375,19 b' class ServerReactorTests(unittest.TestCa' | |||
|
369 | 375 | """Multiple fully serviced commands with same request ID is allowed.""" |
|
370 | 376 | reactor = makereactor() |
|
371 | 377 | results = [] |
|
372 | outstream = framing.stream() | |
|
378 | outstream = framing.stream(2) | |
|
373 | 379 | results.append(self._sendsingleframe( |
|
374 | reactor, ffs(b'1 command-name eos command'))) | |
|
380 | reactor, ffs(b'1 1 stream-begin command-name eos command'))) | |
|
375 | 381 | result = reactor.onbytesresponseready(outstream, 1, b'response1') |
|
376 | 382 | self.assertaction(result, 'sendframes') |
|
377 | 383 | list(result[1]['framegen']) |
|
378 | 384 | results.append(self._sendsingleframe( |
|
379 | reactor, ffs(b'1 command-name eos command'))) | |
|
385 | reactor, ffs(b'1 1 0 command-name eos command'))) | |
|
380 | 386 | result = reactor.onbytesresponseready(outstream, 1, b'response2') |
|
381 | 387 | self.assertaction(result, 'sendframes') |
|
382 | 388 | list(result[1]['framegen']) |
|
383 | 389 | results.append(self._sendsingleframe( |
|
384 | reactor, ffs(b'1 command-name eos command'))) | |
|
390 | reactor, ffs(b'1 1 0 command-name eos command'))) | |
|
385 | 391 | result = reactor.onbytesresponseready(outstream, 1, b'response3') |
|
386 | 392 | self.assertaction(result, 'sendframes') |
|
387 | 393 | list(result[1]['framegen']) |
@@ -398,8 +404,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
398 | 404 | def testconflictingrequestid(self): |
|
399 | 405 | """Request ID for new command matching in-flight command is illegal.""" |
|
400 | 406 | results = list(sendframes(makereactor(), [ |
|
401 | ffs(b'1 command-name have-args command'), | |
|
402 | ffs(b'1 command-name eos command'), | |
|
407 | ffs(b'1 1 stream-begin command-name have-args command'), | |
|
408 | ffs(b'1 1 0 command-name eos command'), | |
|
403 | 409 | ])) |
|
404 | 410 | |
|
405 | 411 | self.assertaction(results[0], 'wantframe') |
@@ -410,12 +416,12 b' class ServerReactorTests(unittest.TestCa' | |||
|
410 | 416 | |
|
411 | 417 | def testinterleavedcommands(self): |
|
412 | 418 | results = list(sendframes(makereactor(), [ |
|
413 | ffs(b'1 command-name have-args command1'), | |
|
414 | ffs(b'3 command-name have-args command3'), | |
|
415 | ffs(br'1 command-argument 0 \x03\x00\x03\x00foobar'), | |
|
416 | ffs(br'3 command-argument 0 \x03\x00\x03\x00bizbaz'), | |
|
417 | ffs(br'3 command-argument eoa \x03\x00\x03\x00keyval'), | |
|
418 | ffs(br'1 command-argument eoa \x04\x00\x03\x00key1val'), | |
|
419 | ffs(b'1 1 stream-begin command-name have-args command1'), | |
|
420 | ffs(b'3 1 0 command-name have-args command3'), | |
|
421 | ffs(br'1 1 0 command-argument 0 \x03\x00\x03\x00foobar'), | |
|
422 | ffs(br'3 1 0 command-argument 0 \x03\x00\x03\x00bizbaz'), | |
|
423 | ffs(br'3 1 0 command-argument eoa \x03\x00\x03\x00keyval'), | |
|
424 | ffs(br'1 1 0 command-argument eoa \x04\x00\x03\x00key1val'), | |
|
419 | 425 | ])) |
|
420 | 426 | |
|
421 | 427 | self.assertEqual([t[0] for t in results], [ |
@@ -445,7 +451,7 b' class ServerReactorTests(unittest.TestCa' | |||
|
445 | 451 | # command request waiting on argument data. But it doesn't handle that |
|
446 | 452 | # scenario yet. So this test does nothing of value. |
|
447 | 453 | frames = [ |
|
448 | ffs(b'1 command-name have-args command'), | |
|
454 | ffs(b'1 1 stream-begin command-name have-args command'), | |
|
449 | 455 | ] |
|
450 | 456 | |
|
451 | 457 | results = list(sendframes(makereactor(), frames)) |
@@ -454,8 +460,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
454 | 460 | def testincompleteargumentname(self): |
|
455 | 461 | """Argument frame with incomplete name.""" |
|
456 | 462 | frames = [ |
|
457 | ffs(b'1 command-name have-args command1'), | |
|
458 | ffs(br'1 command-argument eoa \x04\x00\xde\xadfoo'), | |
|
463 | ffs(b'1 1 stream-begin command-name have-args command1'), | |
|
464 | ffs(br'1 1 0 command-argument eoa \x04\x00\xde\xadfoo'), | |
|
459 | 465 | ] |
|
460 | 466 | |
|
461 | 467 | results = list(sendframes(makereactor(), frames)) |
@@ -469,8 +475,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
469 | 475 | def testincompleteargumentvalue(self): |
|
470 | 476 | """Argument frame with incomplete value.""" |
|
471 | 477 | frames = [ |
|
472 | ffs(b'1 command-name have-args command'), | |
|
473 | ffs(br'1 command-argument eoa \x03\x00\xaa\xaafoopartialvalue'), | |
|
478 | ffs(b'1 1 stream-begin command-name have-args command'), | |
|
479 | ffs(br'1 1 0 command-argument eoa \x03\x00\xaa\xaafoopartialvalue'), | |
|
474 | 480 | ] |
|
475 | 481 | |
|
476 | 482 | results = list(sendframes(makereactor(), frames)) |
@@ -485,8 +491,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
485 | 491 | # The reactor doesn't currently handle partially received commands. |
|
486 | 492 | # So this test is failing to do anything with request 1. |
|
487 | 493 | frames = [ |
|
488 | ffs(b'1 command-name have-data command1'), | |
|
489 | ffs(b'3 command-name eos command2'), | |
|
494 | ffs(b'1 1 stream-begin command-name have-data command1'), | |
|
495 | ffs(b'3 1 0 command-name eos command2'), | |
|
490 | 496 | ] |
|
491 | 497 | results = list(sendframes(makereactor(), frames)) |
|
492 | 498 | self.assertEqual(len(results), 2) |
@@ -495,8 +501,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
495 | 501 | |
|
496 | 502 | def testmissingcommanddataframeflags(self): |
|
497 | 503 | frames = [ |
|
498 | ffs(b'1 command-name have-data command1'), | |
|
499 | ffs(b'1 command-data 0 data'), | |
|
504 | ffs(b'1 1 stream-begin command-name have-data command1'), | |
|
505 | ffs(b'1 1 0 command-data 0 data'), | |
|
500 | 506 | ] |
|
501 | 507 | results = list(sendframes(makereactor(), frames)) |
|
502 | 508 | self.assertEqual(len(results), 2) |
@@ -509,9 +515,9 b' class ServerReactorTests(unittest.TestCa' | |||
|
509 | 515 | def testframefornonreceivingrequest(self): |
|
510 | 516 | """Receiving a frame for a command that is not receiving is illegal.""" |
|
511 | 517 | results = list(sendframes(makereactor(), [ |
|
512 | ffs(b'1 command-name eos command1'), | |
|
513 | ffs(b'3 command-name have-data command3'), | |
|
514 | ffs(b'5 command-argument eoa ignored'), | |
|
518 | ffs(b'1 1 stream-begin command-name eos command1'), | |
|
519 | ffs(b'3 1 0 command-name have-data command3'), | |
|
520 | ffs(b'5 1 0 command-argument eoa ignored'), | |
|
515 | 521 | ])) |
|
516 | 522 | self.assertaction(results[2], 'error') |
|
517 | 523 | self.assertEqual(results[2][1], { |
@@ -521,14 +527,14 b' class ServerReactorTests(unittest.TestCa' | |||
|
521 | 527 | def testsimpleresponse(self): |
|
522 | 528 | """Bytes response to command sends result frames.""" |
|
523 | 529 | reactor = makereactor() |
|
524 | instream = framing.stream() | |
|
530 | instream = framing.stream(1) | |
|
525 | 531 | list(sendcommandframes(reactor, instream, 1, b'mycommand', {})) |
|
526 | 532 | |
|
527 | outstream = framing.stream() | |
|
533 | outstream = framing.stream(2) | |
|
528 | 534 | result = reactor.onbytesresponseready(outstream, 1, b'response') |
|
529 | 535 | self.assertaction(result, 'sendframes') |
|
530 | 536 | self.assertframesequal(result[1]['framegen'], [ |
|
531 | b'1 bytes-response eos response', | |
|
537 | b'1 2 stream-begin bytes-response eos response', | |
|
532 | 538 | ]) |
|
533 | 539 | |
|
534 | 540 | def testmultiframeresponse(self): |
@@ -537,54 +543,54 b' class ServerReactorTests(unittest.TestCa' | |||
|
537 | 543 | second = b'y' * 100 |
|
538 | 544 | |
|
539 | 545 | reactor = makereactor() |
|
540 | instream = framing.stream() | |
|
546 | instream = framing.stream(1) | |
|
541 | 547 | list(sendcommandframes(reactor, instream, 1, b'mycommand', {})) |
|
542 | 548 | |
|
543 | outstream = framing.stream() | |
|
549 | outstream = framing.stream(2) | |
|
544 | 550 | result = reactor.onbytesresponseready(outstream, 1, first + second) |
|
545 | 551 | self.assertaction(result, 'sendframes') |
|
546 | 552 | self.assertframesequal(result[1]['framegen'], [ |
|
547 | b'1 bytes-response continuation %s' % first, | |
|
548 | b'1 bytes-response eos %s' % second, | |
|
553 | b'1 2 stream-begin bytes-response continuation %s' % first, | |
|
554 | b'1 2 0 bytes-response eos %s' % second, | |
|
549 | 555 | ]) |
|
550 | 556 | |
|
551 | 557 | def testapplicationerror(self): |
|
552 | 558 | reactor = makereactor() |
|
553 | instream = framing.stream() | |
|
559 | instream = framing.stream(1) | |
|
554 | 560 | list(sendcommandframes(reactor, instream, 1, b'mycommand', {})) |
|
555 | 561 | |
|
556 | outstream = framing.stream() | |
|
562 | outstream = framing.stream(2) | |
|
557 | 563 | result = reactor.onapplicationerror(outstream, 1, b'some message') |
|
558 | 564 | self.assertaction(result, 'sendframes') |
|
559 | 565 | self.assertframesequal(result[1]['framegen'], [ |
|
560 | b'1 error-response application some message', | |
|
566 | b'1 2 stream-begin error-response application some message', | |
|
561 | 567 | ]) |
|
562 | 568 | |
|
563 | 569 | def test1commanddeferresponse(self): |
|
564 | 570 | """Responses when in deferred output mode are delayed until EOF.""" |
|
565 | 571 | reactor = makereactor(deferoutput=True) |
|
566 | instream = framing.stream() | |
|
572 | instream = framing.stream(1) | |
|
567 | 573 | results = list(sendcommandframes(reactor, instream, 1, b'mycommand', |
|
568 | 574 | {})) |
|
569 | 575 | self.assertEqual(len(results), 1) |
|
570 | 576 | self.assertaction(results[0], 'runcommand') |
|
571 | 577 | |
|
572 | outstream = framing.stream() | |
|
578 | outstream = framing.stream(2) | |
|
573 | 579 | result = reactor.onbytesresponseready(outstream, 1, b'response') |
|
574 | 580 | self.assertaction(result, 'noop') |
|
575 | 581 | result = reactor.oninputeof() |
|
576 | 582 | self.assertaction(result, 'sendframes') |
|
577 | 583 | self.assertframesequal(result[1]['framegen'], [ |
|
578 | b'1 bytes-response eos response', | |
|
584 | b'1 2 stream-begin bytes-response eos response', | |
|
579 | 585 | ]) |
|
580 | 586 | |
|
581 | 587 | def testmultiplecommanddeferresponse(self): |
|
582 | 588 | reactor = makereactor(deferoutput=True) |
|
583 | instream = framing.stream() | |
|
589 | instream = framing.stream(1) | |
|
584 | 590 | list(sendcommandframes(reactor, instream, 1, b'command1', {})) |
|
585 | 591 | list(sendcommandframes(reactor, instream, 3, b'command2', {})) |
|
586 | 592 | |
|
587 | outstream = framing.stream() | |
|
593 | outstream = framing.stream(2) | |
|
588 | 594 | result = reactor.onbytesresponseready(outstream, 1, b'response1') |
|
589 | 595 | self.assertaction(result, 'noop') |
|
590 | 596 | result = reactor.onbytesresponseready(outstream, 3, b'response2') |
@@ -592,19 +598,19 b' class ServerReactorTests(unittest.TestCa' | |||
|
592 | 598 | result = reactor.oninputeof() |
|
593 | 599 | self.assertaction(result, 'sendframes') |
|
594 | 600 | self.assertframesequal(result[1]['framegen'], [ |
|
595 | b'1 bytes-response eos response1', | |
|
596 | b'3 bytes-response eos response2' | |
|
601 | b'1 2 stream-begin bytes-response eos response1', | |
|
602 | b'3 2 0 bytes-response eos response2' | |
|
597 | 603 | ]) |
|
598 | 604 | |
|
599 | 605 | def testrequestidtracking(self): |
|
600 | 606 | reactor = makereactor(deferoutput=True) |
|
601 | instream = framing.stream() | |
|
607 | instream = framing.stream(1) | |
|
602 | 608 | list(sendcommandframes(reactor, instream, 1, b'command1', {})) |
|
603 | 609 | list(sendcommandframes(reactor, instream, 3, b'command2', {})) |
|
604 | 610 | list(sendcommandframes(reactor, instream, 5, b'command3', {})) |
|
605 | 611 | |
|
606 | 612 | # Register results for commands out of order. |
|
607 | outstream = framing.stream() | |
|
613 | outstream = framing.stream(2) | |
|
608 | 614 | reactor.onbytesresponseready(outstream, 3, b'response3') |
|
609 | 615 | reactor.onbytesresponseready(outstream, 1, b'response1') |
|
610 | 616 | reactor.onbytesresponseready(outstream, 5, b'response5') |
@@ -612,15 +618,15 b' class ServerReactorTests(unittest.TestCa' | |||
|
612 | 618 | result = reactor.oninputeof() |
|
613 | 619 | self.assertaction(result, 'sendframes') |
|
614 | 620 | self.assertframesequal(result[1]['framegen'], [ |
|
615 | b'3 bytes-response eos response3', | |
|
616 | b'1 bytes-response eos response1', | |
|
617 | b'5 bytes-response eos response5', | |
|
621 | b'3 2 stream-begin bytes-response eos response3', | |
|
622 | b'1 2 0 bytes-response eos response1', | |
|
623 | b'5 2 0 bytes-response eos response5', | |
|
618 | 624 | ]) |
|
619 | 625 | |
|
620 | 626 | def testduplicaterequestonactivecommand(self): |
|
621 | 627 | """Receiving a request ID that matches a request that isn't finished.""" |
|
622 | 628 | reactor = makereactor() |
|
623 | stream = framing.stream() | |
|
629 | stream = framing.stream(1) | |
|
624 | 630 | list(sendcommandframes(reactor, stream, 1, b'command1', {})) |
|
625 | 631 | results = list(sendcommandframes(reactor, stream, 1, b'command1', {})) |
|
626 | 632 | |
@@ -632,9 +638,9 b' class ServerReactorTests(unittest.TestCa' | |||
|
632 | 638 | def testduplicaterequestonactivecommandnosend(self): |
|
633 | 639 | """Same as above but we've registered a response but haven't sent it.""" |
|
634 | 640 | reactor = makereactor() |
|
635 | instream = framing.stream() | |
|
641 | instream = framing.stream(1) | |
|
636 | 642 | list(sendcommandframes(reactor, instream, 1, b'command1', {})) |
|
637 | outstream = framing.stream() | |
|
643 | outstream = framing.stream(2) | |
|
638 | 644 | reactor.onbytesresponseready(outstream, 1, b'response') |
|
639 | 645 | |
|
640 | 646 | # We've registered the response but haven't sent it. From the |
@@ -649,11 +655,11 b' class ServerReactorTests(unittest.TestCa' | |||
|
649 | 655 | def testduplicaterequestargumentframe(self): |
|
650 | 656 | """Variant on above except we sent an argument frame instead of name.""" |
|
651 | 657 | reactor = makereactor() |
|
652 | stream = framing.stream() | |
|
658 | stream = framing.stream(1) | |
|
653 | 659 | list(sendcommandframes(reactor, stream, 1, b'command', {})) |
|
654 | 660 | results = list(sendframes(reactor, [ |
|
655 | ffs(b'3 command-name have-args command'), | |
|
656 | ffs(b'1 command-argument 0 ignored'), | |
|
661 | ffs(b'3 1 stream-begin command-name have-args command'), | |
|
662 | ffs(b'1 1 0 command-argument 0 ignored'), | |
|
657 | 663 | ])) |
|
658 | 664 | self.assertaction(results[0], 'wantframe') |
|
659 | 665 | self.assertaction(results[1], 'error') |
@@ -664,9 +670,9 b' class ServerReactorTests(unittest.TestCa' | |||
|
664 | 670 | def testduplicaterequestaftersend(self): |
|
665 | 671 | """We can use a duplicate request ID after we've sent the response.""" |
|
666 | 672 | reactor = makereactor() |
|
667 | instream = framing.stream() | |
|
673 | instream = framing.stream(1) | |
|
668 | 674 | list(sendcommandframes(reactor, instream, 1, b'command1', {})) |
|
669 | outstream = framing.stream() | |
|
675 | outstream = framing.stream(2) | |
|
670 | 676 | res = reactor.onbytesresponseready(outstream, 1, b'response') |
|
671 | 677 | list(res[1]['framegen']) |
|
672 | 678 |
General Comments 0
You need to be logged in to leave comments.
Login now