##// END OF EJS Templates
wireproto: define and implement protocol for issuing requests...
Gregory Szorc -
r37069:40206e22 default
parent child Browse files
Show More
@@ -0,0 +1,156 b''
1 # wireprotoframing.py - unified framing protocol for wire protocol
2 #
3 # Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7
8 # This file contains functionality to support the unified frame-based wire
9 # protocol. For details about the protocol, see
10 # `hg help internals.wireprotocol`.
11
12 from __future__ import absolute_import
13
14 import struct
15
16 from . import (
17 util,
18 )
19
20 FRAME_HEADER_SIZE = 4
21 DEFAULT_MAX_FRAME_SIZE = 32768
22
23 FRAME_TYPE_COMMAND_NAME = 0x01
24 FRAME_TYPE_COMMAND_ARGUMENT = 0x02
25 FRAME_TYPE_COMMAND_DATA = 0x03
26
27 FRAME_TYPES = {
28 b'command-name': FRAME_TYPE_COMMAND_NAME,
29 b'command-argument': FRAME_TYPE_COMMAND_ARGUMENT,
30 b'command-data': FRAME_TYPE_COMMAND_DATA,
31 }
32
33 FLAG_COMMAND_NAME_EOS = 0x01
34 FLAG_COMMAND_NAME_HAVE_ARGS = 0x02
35 FLAG_COMMAND_NAME_HAVE_DATA = 0x04
36
37 FLAGS_COMMAND = {
38 b'eos': FLAG_COMMAND_NAME_EOS,
39 b'have-args': FLAG_COMMAND_NAME_HAVE_ARGS,
40 b'have-data': FLAG_COMMAND_NAME_HAVE_DATA,
41 }
42
43 FLAG_COMMAND_ARGUMENT_CONTINUATION = 0x01
44 FLAG_COMMAND_ARGUMENT_EOA = 0x02
45
46 FLAGS_COMMAND_ARGUMENT = {
47 b'continuation': FLAG_COMMAND_ARGUMENT_CONTINUATION,
48 b'eoa': FLAG_COMMAND_ARGUMENT_EOA,
49 }
50
51 FLAG_COMMAND_DATA_CONTINUATION = 0x01
52 FLAG_COMMAND_DATA_EOS = 0x02
53
54 FLAGS_COMMAND_DATA = {
55 b'continuation': FLAG_COMMAND_DATA_CONTINUATION,
56 b'eos': FLAG_COMMAND_DATA_EOS,
57 }
58
59 # Maps frame types to their available flags.
60 FRAME_TYPE_FLAGS = {
61 FRAME_TYPE_COMMAND_NAME: FLAGS_COMMAND,
62 FRAME_TYPE_COMMAND_ARGUMENT: FLAGS_COMMAND_ARGUMENT,
63 FRAME_TYPE_COMMAND_DATA: FLAGS_COMMAND_DATA,
64 }
65
66 ARGUMENT_FRAME_HEADER = struct.Struct(r'<HH')
67
68 def makeframe(frametype, frameflags, payload):
69 """Assemble a frame into a byte array."""
70 # TODO assert size of payload.
71 frame = bytearray(FRAME_HEADER_SIZE + len(payload))
72
73 l = struct.pack(r'<I', len(payload))
74 frame[0:3] = l[0:3]
75 frame[3] = (frametype << 4) | frameflags
76 frame[4:] = payload
77
78 return frame
79
80 def makeframefromhumanstring(s):
81 """Given a string of the form: <type> <flags> <payload>, creates a frame.
82
83 This can be used by user-facing applications and tests for creating
84 frames easily without having to type out a bunch of constants.
85
86 Frame type and flags can be specified by integer or named constant.
87 Flags can be delimited by `|` to bitwise OR them together.
88 """
89 frametype, frameflags, payload = s.split(b' ', 2)
90
91 if frametype in FRAME_TYPES:
92 frametype = FRAME_TYPES[frametype]
93 else:
94 frametype = int(frametype)
95
96 finalflags = 0
97 validflags = FRAME_TYPE_FLAGS[frametype]
98 for flag in frameflags.split(b'|'):
99 if flag in validflags:
100 finalflags |= validflags[flag]
101 else:
102 finalflags |= int(flag)
103
104 payload = util.unescapestr(payload)
105
106 return makeframe(frametype, finalflags, payload)
107
108 def createcommandframes(cmd, args, datafh=None):
109 """Create frames necessary to transmit a request to run a command.
110
111 This is a generator of bytearrays. Each item represents a frame
112 ready to be sent over the wire to a peer.
113 """
114 flags = 0
115 if args:
116 flags |= FLAG_COMMAND_NAME_HAVE_ARGS
117 if datafh:
118 flags |= FLAG_COMMAND_NAME_HAVE_DATA
119
120 if not flags:
121 flags |= FLAG_COMMAND_NAME_EOS
122
123 yield makeframe(FRAME_TYPE_COMMAND_NAME, flags, cmd)
124
125 for i, k in enumerate(sorted(args)):
126 v = args[k]
127 last = i == len(args) - 1
128
129 # TODO handle splitting of argument values across frames.
130 payload = bytearray(ARGUMENT_FRAME_HEADER.size + len(k) + len(v))
131 offset = 0
132 ARGUMENT_FRAME_HEADER.pack_into(payload, offset, len(k), len(v))
133 offset += ARGUMENT_FRAME_HEADER.size
134 payload[offset:offset + len(k)] = k
135 offset += len(k)
136 payload[offset:offset + len(v)] = v
137
138 flags = FLAG_COMMAND_ARGUMENT_EOA if last else 0
139 yield makeframe(FRAME_TYPE_COMMAND_ARGUMENT, flags, payload)
140
141 if datafh:
142 while True:
143 data = datafh.read(DEFAULT_MAX_FRAME_SIZE)
144
145 done = False
146 if len(data) == DEFAULT_MAX_FRAME_SIZE:
147 flags = FLAG_COMMAND_DATA_CONTINUATION
148 else:
149 flags = FLAG_COMMAND_DATA_EOS
150 assert datafh.read(1) == b''
151 done = True
152
153 yield makeframe(FRAME_TYPE_COMMAND_DATA, flags, data)
154
155 if done:
156 break
@@ -78,6 +78,7 b' from . import ('
78 url as urlmod,
78 url as urlmod,
79 util,
79 util,
80 vfs as vfsmod,
80 vfs as vfsmod,
81 wireprotoframing,
81 wireprotoserver,
82 wireprotoserver,
82 )
83 )
83 from .utils import dateutil
84 from .utils import dateutil
@@ -2711,6 +2712,12 b' def debugwireproto(ui, repo, path=None, '
2711 The content of the file defined as the value to this argument will be
2712 The content of the file defined as the value to this argument will be
2712 transferred verbatim as the HTTP request body.
2713 transferred verbatim as the HTTP request body.
2713
2714
2715 ``frame <type> <flags> <payload>``
2716 Send a unified protocol frame as part of the request body.
2717
2718 All frames will be collected and sent as the body to the HTTP
2719 request.
2720
2714 close
2721 close
2715 -----
2722 -----
2716
2723
@@ -2750,6 +2757,28 b' def debugwireproto(ui, repo, path=None, '
2750 ---------
2757 ---------
2751
2758
2752 ``read()`` N bytes from the server's stderr pipe, if available.
2759 ``read()`` N bytes from the server's stderr pipe, if available.
2760
2761 Specifying Unified Frame-Based Protocol Frames
2762 ----------------------------------------------
2763
2764 It is possible to emit a *Unified Frame-Based Protocol* by using special
2765 syntax.
2766
2767 A frame is composed as a type, flags, and payload. These can be parsed
2768 from a string of the form ``<type> <flags> <payload>``. That is, 3
2769 space-delimited strings.
2770
2771 ``payload`` is the simplest: it is evaluated as a Python byte string
2772 literal.
2773
2774 ``type`` can be an integer value for the frame type or the string name
2775 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2776 ``command-name``.
2777
2778 ``flags`` is a ``|`` delimited list of flag components. Each component
2779 (and there can be just one) can be an integer or a flag name for the
2780 specified frame type. Values are resolved to integers and then bitwise
2781 OR'd together.
2753 """
2782 """
2754 opts = pycompat.byteskwargs(opts)
2783 opts = pycompat.byteskwargs(opts)
2755
2784
@@ -2953,6 +2982,7 b' def debugwireproto(ui, repo, path=None, '
2953 method, httppath = request[1:]
2982 method, httppath = request[1:]
2954 headers = {}
2983 headers = {}
2955 body = None
2984 body = None
2985 frames = []
2956 for line in lines:
2986 for line in lines:
2957 line = line.lstrip()
2987 line = line.lstrip()
2958 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
2988 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
@@ -2963,11 +2993,20 b' def debugwireproto(ui, repo, path=None, '
2963 if line.startswith(b'BODYFILE '):
2993 if line.startswith(b'BODYFILE '):
2964 with open(line.split(b' ', 1), 'rb') as fh:
2994 with open(line.split(b' ', 1), 'rb') as fh:
2965 body = fh.read()
2995 body = fh.read()
2996 elif line.startswith(b'frame '):
2997 frame = wireprotoframing.makeframefromhumanstring(
2998 line[len(b'frame '):])
2999
3000 frames.append(frame)
2966 else:
3001 else:
2967 raise error.Abort(_('unknown argument to httprequest: %s') %
3002 raise error.Abort(_('unknown argument to httprequest: %s') %
2968 line)
3003 line)
2969
3004
2970 url = path + httppath
3005 url = path + httppath
3006
3007 if frames:
3008 body = b''.join(bytes(f) for f in frames)
3009
2971 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3010 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
2972
3011
2973 # urllib.Request insists on using has_data() as a proxy for
3012 # urllib.Request insists on using has_data() as a proxy for
@@ -187,12 +187,15 b' exchange takes place. This provides a be'
187 Requests to unknown commands or URLS result in an HTTP 404.
187 Requests to unknown commands or URLS result in an HTTP 404.
188 TODO formally define response type, how error is communicated, etc.
188 TODO formally define response type, how error is communicated, etc.
189
189
190 HTTP request and response bodies use the *TBD Protocol* for media exchange.
190 HTTP request and response bodies use the *Unified Frame-Based Protocol*
191 (defined below) for media exchange. The entirety of the HTTP message
192 body is 0 or more frames as defined by this protocol.
191
193
192 Clients and servers MUST advertise the ``TBD`` media type via the
194 Clients and servers MUST advertise the ``TBD`` media type via the
193 ``Content-Type`` request and response headers. In addition, clients MUST
195 ``Content-Type`` request and response headers. In addition, clients MUST
194 advertise this media type value in their ``Accept`` request header in all
196 advertise this media type value in their ``Accept`` request header in all
195 requests.
197 requests.
198 TODO finalize the media type. For now, it is defined in wireprotoserver.py.
196
199
197 Servers receiving requests without an ``Accept`` header SHOULD respond with
200 Servers receiving requests without an ``Accept`` header SHOULD respond with
198 an HTTP 406.
201 an HTTP 406.
@@ -429,7 +432,7 b' The server terminates if it receives an '
429 SSH Version 2 Transport
432 SSH Version 2 Transport
430 -----------------------
433 -----------------------
431
434
432 **Experimental**
435 **Experimental and under development**
433
436
434 Version 2 of the SSH transport behaves identically to version 1 of the SSH
437 Version 2 of the SSH transport behaves identically to version 1 of the SSH
435 transport with the exception of handshake semantics. See above for how
438 transport with the exception of handshake semantics. See above for how
@@ -451,6 +454,164 b' e.g.'
451 Following capabilities advertisement, the peers communicate using version
454 Following capabilities advertisement, the peers communicate using version
452 1 of the SSH transport.
455 1 of the SSH transport.
453
456
457 Unified Frame-Based Protocol
458 ============================
459
460 **Experimental and under development**
461
462 The *Unified Frame-Based Protocol* is a communications protocol between
463 Mercurial peers. The protocol aims to be mostly transport agnostic
464 (works similarly on HTTP, SSH, etc).
465
466 To operate the protocol, a bi-directional, half-duplex pipe supporting
467 ordered sends and receives is required. That is, each peer has one pipe
468 for sending data and another for receiving.
469
470 The protocol is request-response based: the client issues requests to
471 the server, which issues replies to those requests. Server-initiated
472 messaging is not supported.
473
474 All data is read and written in atomic units called *frames*. These
475 are conceptually similar to TCP packets. Higher-level functionality
476 is built on the exchange and processing of frames.
477
478 Frames begin with a 4 octet header followed by a variable length
479 payload::
480
481 +-----------------------------------------------+
482 | Length (24) |
483 +-----------+-----------------------------------+
484 | Type (4) |
485 +-----------+
486 | Flags (4) |
487 +===========+===================================================|
488 | Frame Payload (0...) ...
489 +---------------------------------------------------------------+
490
491 The length of the frame payload is expressed as an unsigned 24 bit
492 little endian integer. Values larger than 65535 MUST NOT be used unless
493 given permission by the server as part of the negotiated capabilities
494 during the handshake. The frame header is not part of the advertised
495 frame length.
496
497 The 4-bit ``Type`` field denotes the type of message being sent.
498
499 The 4-bit ``Flags`` field defines special, per-type attributes for
500 the frame.
501
502 The sections below define the frame types and their behavior.
503
504 Command Request (``0x01``)
505 --------------------------
506
507 This frame contains a request to run a command.
508
509 The name of the command to run constitutes the entirety of the frame
510 payload.
511
512 This frame type MUST ONLY be sent from clients to servers: it is illegal
513 for a server to send this frame to a client.
514
515 The following flag values are defined for this type:
516
517 0x01
518 End of command data. When set, the client will not send any command
519 arguments or additional command data. When set, the command has been
520 fully issued and the server has the full context to process the command.
521 The next frame issued by the client is not part of this command.
522 0x02
523 Command argument frames expected. When set, the client will send
524 *Command Argument* frames containing command argument data.
525 0x04
526 Command data frames expected. When set, the client will send
527 *Command Data* frames containing a raw stream of data for this
528 command.
529
530 The ``0x01`` flag is mutually exclusive with both the ``0x02`` and ``0x04``
531 flags.
532
533 Command Argument (``0x02``)
534 ---------------------------
535
536 This frame contains a named argument for a command.
537
538 The frame type MUST ONLY be sent from clients to servers: it is illegal
539 for a server to send this frame to a client.
540
541 The payload consists of:
542
543 * A 16-bit little endian integer denoting the length of the
544 argument name.
545 * A 16-bit little endian integer denoting the length of the
546 argument value.
547 * N bytes of ASCII data containing the argument name.
548 * N bytes of binary data containing the argument value.
549
550 The payload MUST hold the entirety of the 32-bit header and the
551 argument name. The argument value MAY span multiple frames. If this
552 occurs, the appropriate frame flag should be set to indicate this.
553
554 The following flag values are defined for this type:
555
556 0x01
557 Argument data continuation. When set, the data for this argument did
558 not fit in a single frame and the next frame will contain additional
559 argument data.
560
561 0x02
562 End of arguments data. When set, the client will not send any more
563 command arguments for the command this frame is associated with.
564 The next frame issued by the client will be command data or
565 belong to a separate request.
566
567 Command Data (``0x03``)
568 -----------------------
569
570 This frame contains raw data for a command.
571
572 Most commands can be executed by specifying arguments. However,
573 arguments have an upper bound to their length. For commands that
574 accept data that is beyond this length or whose length isn't known
575 when the command is initially sent, they will need to stream
576 arbitrary data to the server. This frame type facilitates the sending
577 of this data.
578
579 The payload of this frame type consists of a stream of raw data to be
580 consumed by the command handler on the server. The format of the data
581 is command specific.
582
583 The following flag values are defined for this type:
584
585 0x01
586 Command data continuation. When set, the data for this command
587 continues into a subsequent frame.
588
589 0x02
590 End of data. When set, command data has been fully sent to the
591 server. The command has been fully issued and no new data for this
592 command will be sent. The next frame will belong to a new command.
593
594 Issuing Commands
595 ----------------
596
597 A client can request that a remote run a command by sending it
598 frames defining that command. This logical stream is composed of
599 1 ``Command Request`` frame, 0 or more ``Command Argument`` frames,
600 and 0 or more ``Command Data`` frames.
601
602 Argument frames are the recommended mechanism for transferring fixed
603 sets of parameters to a command. Data frames are appropriate for
604 transferring variable data. A similar comparison would be to HTTP:
605 argument frames are headers and the message body is data frames.
606
607 It is recommended for servers to delay the dispatch of a command
608 until all argument frames for that command have been received. Servers
609 MAY impose limits on the maximum argument size.
610 TODO define failure mechanism.
611
612 Servers MAY dispatch to commands immediately once argument data
613 is available or delay until command data is received in full.
614
454 Capabilities
615 Capabilities
455 ============
616 ============
456
617
@@ -32,7 +32,7 b' HTTP_OK = 200'
32 HGTYPE = 'application/mercurial-0.1'
32 HGTYPE = 'application/mercurial-0.1'
33 HGTYPE2 = 'application/mercurial-0.2'
33 HGTYPE2 = 'application/mercurial-0.2'
34 HGERRTYPE = 'application/hg-error'
34 HGERRTYPE = 'application/hg-error'
35 HTTPV2TYPE = 'application/mercurial-tbd'
35 FRAMINGTYPE = b'application/mercurial-exp-framing-0001'
36
36
37 HTTPV2 = wireprototypes.HTTPV2
37 HTTPV2 = wireprototypes.HTTPV2
38 SSHV1 = wireprototypes.SSHV1
38 SSHV1 = wireprototypes.SSHV1
@@ -336,21 +336,21 b' def _handlehttpv2request(rctx, req, res,'
336 res.setbodybytes(_('invalid wire protocol command: %s') % command)
336 res.setbodybytes(_('invalid wire protocol command: %s') % command)
337 return
337 return
338
338
339 if req.headers.get(b'Accept') != HTTPV2TYPE:
339 if req.headers.get(b'Accept') != FRAMINGTYPE:
340 res.status = b'406 Not Acceptable'
340 res.status = b'406 Not Acceptable'
341 res.headers[b'Content-Type'] = b'text/plain'
341 res.headers[b'Content-Type'] = b'text/plain'
342 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
342 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
343 % HTTPV2TYPE)
343 % FRAMINGTYPE)
344 return
344 return
345
345
346 if (b'Content-Type' in req.headers
346 if (b'Content-Type' in req.headers
347 and req.headers[b'Content-Type'] != HTTPV2TYPE):
347 and req.headers[b'Content-Type'] != FRAMINGTYPE):
348 res.status = b'415 Unsupported Media Type'
348 res.status = b'415 Unsupported Media Type'
349 # TODO we should send a response with appropriate media type,
349 # TODO we should send a response with appropriate media type,
350 # since client does Accept it.
350 # since client does Accept it.
351 res.headers[b'Content-Type'] = b'text/plain'
351 res.headers[b'Content-Type'] = b'text/plain'
352 res.setbodybytes(_('client MUST send Content-Type header with '
352 res.setbodybytes(_('client MUST send Content-Type header with '
353 'value: %s\n') % HTTPV2TYPE)
353 'value: %s\n') % FRAMINGTYPE)
354 return
354 return
355
355
356 # We don't do anything meaningful yet.
356 # We don't do anything meaningful yet.
@@ -1,5 +1,5 b''
1 $ HTTPV2=exp-http-v2-0001
1 $ HTTPV2=exp-http-v2-0001
2 $ MEDIATYPE=application/mercurial-tbd
2 $ MEDIATYPE=application/mercurial-exp-framing-0001
3
3
4 $ send() {
4 $ send() {
5 > hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT/
5 > hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT/
@@ -120,9 +120,9 b' Missing Accept header results in 406'
120 s> Server: testing stub value\r\n
120 s> Server: testing stub value\r\n
121 s> Date: $HTTP_DATE$\r\n
121 s> Date: $HTTP_DATE$\r\n
122 s> Content-Type: text/plain\r\n
122 s> Content-Type: text/plain\r\n
123 s> Content-Length: 72\r\n
123 s> Content-Length: 85\r\n
124 s> \r\n
124 s> \r\n
125 s> client MUST specify Accept header with value: application/mercurial-tbd\n
125 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0001\n
126
126
127 Bad Accept header results in 406
127 Bad Accept header results in 406
128
128
@@ -143,9 +143,9 b' Bad Accept header results in 406'
143 s> Server: testing stub value\r\n
143 s> Server: testing stub value\r\n
144 s> Date: $HTTP_DATE$\r\n
144 s> Date: $HTTP_DATE$\r\n
145 s> Content-Type: text/plain\r\n
145 s> Content-Type: text/plain\r\n
146 s> Content-Length: 72\r\n
146 s> Content-Length: 85\r\n
147 s> \r\n
147 s> \r\n
148 s> client MUST specify Accept header with value: application/mercurial-tbd\n
148 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0001\n
149
149
150 Bad Content-Type header results in 415
150 Bad Content-Type header results in 415
151
151
@@ -158,7 +158,7 b' Bad Content-Type header results in 415'
158 using raw connection to peer
158 using raw connection to peer
159 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
159 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
160 s> Accept-Encoding: identity\r\n
160 s> Accept-Encoding: identity\r\n
161 s> accept: application/mercurial-tbd\r\n
161 s> accept: application/mercurial-exp-framing-0001\r\n
162 s> content-type: badmedia\r\n
162 s> content-type: badmedia\r\n
163 s> user-agent: test\r\n
163 s> user-agent: test\r\n
164 s> host: $LOCALIP:$HGPORT\r\n (glob)
164 s> host: $LOCALIP:$HGPORT\r\n (glob)
@@ -168,9 +168,9 b' Bad Content-Type header results in 415'
168 s> Server: testing stub value\r\n
168 s> Server: testing stub value\r\n
169 s> Date: $HTTP_DATE$\r\n
169 s> Date: $HTTP_DATE$\r\n
170 s> Content-Type: text/plain\r\n
170 s> Content-Type: text/plain\r\n
171 s> Content-Length: 75\r\n
171 s> Content-Length: 88\r\n
172 s> \r\n
172 s> \r\n
173 s> client MUST send Content-Type header with value: application/mercurial-tbd\n
173 s> client MUST send Content-Type header with value: application/mercurial-exp-framing-0001\n
174
174
175 Request to read-only command works out of the box
175 Request to read-only command works out of the box
176
176
@@ -179,15 +179,18 b' Request to read-only command works out o'
179 > accept: $MEDIATYPE
179 > accept: $MEDIATYPE
180 > content-type: $MEDIATYPE
180 > content-type: $MEDIATYPE
181 > user-agent: test
181 > user-agent: test
182 > frame command-name eos customreadonly
182 > EOF
183 > EOF
183 using raw connection to peer
184 using raw connection to peer
184 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
185 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
185 s> Accept-Encoding: identity\r\n
186 s> Accept-Encoding: identity\r\n
186 s> accept: application/mercurial-tbd\r\n
187 s> accept: application/mercurial-exp-framing-0001\r\n
187 s> content-type: application/mercurial-tbd\r\n
188 s> content-type: application/mercurial-exp-framing-0001\r\n
188 s> user-agent: test\r\n
189 s> user-agent: test\r\n
190 s> content-length: 18\r\n
189 s> host: $LOCALIP:$HGPORT\r\n (glob)
191 s> host: $LOCALIP:$HGPORT\r\n (glob)
190 s> \r\n
192 s> \r\n
193 s> \x0e\x00\x00\x11customreadonly
191 s> makefile('rb', None)
194 s> makefile('rb', None)
192 s> HTTP/1.1 200 OK\r\n
195 s> HTTP/1.1 200 OK\r\n
193 s> Server: testing stub value\r\n
196 s> Server: testing stub value\r\n
@@ -283,15 +286,18 b' Authorized request for valid read-write '
283 > user-agent: test
286 > user-agent: test
284 > accept: $MEDIATYPE
287 > accept: $MEDIATYPE
285 > content-type: $MEDIATYPE
288 > content-type: $MEDIATYPE
289 > frame command-name eos customreadonly
286 > EOF
290 > EOF
287 using raw connection to peer
291 using raw connection to peer
288 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
292 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
289 s> Accept-Encoding: identity\r\n
293 s> Accept-Encoding: identity\r\n
290 s> accept: application/mercurial-tbd\r\n
294 s> accept: application/mercurial-exp-framing-0001\r\n
291 s> content-type: application/mercurial-tbd\r\n
295 s> content-type: application/mercurial-exp-framing-0001\r\n
292 s> user-agent: test\r\n
296 s> user-agent: test\r\n
297 s> content-length: 18\r\n
293 s> host: $LOCALIP:$HGPORT\r\n (glob)
298 s> host: $LOCALIP:$HGPORT\r\n (glob)
294 s> \r\n
299 s> \r\n
300 s> \x0e\x00\x00\x11customreadonly
295 s> makefile('rb', None)
301 s> makefile('rb', None)
296 s> HTTP/1.1 200 OK\r\n
302 s> HTTP/1.1 200 OK\r\n
297 s> Server: testing stub value\r\n
303 s> Server: testing stub value\r\n
@@ -311,7 +317,7 b' Authorized request for unknown command i'
311 using raw connection to peer
317 using raw connection to peer
312 s> POST /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
318 s> POST /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
313 s> Accept-Encoding: identity\r\n
319 s> Accept-Encoding: identity\r\n
314 s> accept: application/mercurial-tbd\r\n
320 s> accept: application/mercurial-exp-framing-0001\r\n
315 s> user-agent: test\r\n
321 s> user-agent: test\r\n
316 s> host: $LOCALIP:$HGPORT\r\n (glob)
322 s> host: $LOCALIP:$HGPORT\r\n (glob)
317 s> \r\n
323 s> \r\n
General Comments 0
You need to be logged in to leave comments. Login now