mercurial/wireprotoframing.py
changeset 37060 0a6c5cc09a88
parent 37058 c5e9c3b47366
child 37061 884a0c1604ad
equal deleted inserted replaced
37059:bbea991635d0 37060:0a6c5cc09a88
    25 FRAME_TYPE_COMMAND_NAME = 0x01
    25 FRAME_TYPE_COMMAND_NAME = 0x01
    26 FRAME_TYPE_COMMAND_ARGUMENT = 0x02
    26 FRAME_TYPE_COMMAND_ARGUMENT = 0x02
    27 FRAME_TYPE_COMMAND_DATA = 0x03
    27 FRAME_TYPE_COMMAND_DATA = 0x03
    28 FRAME_TYPE_BYTES_RESPONSE = 0x04
    28 FRAME_TYPE_BYTES_RESPONSE = 0x04
    29 FRAME_TYPE_ERROR_RESPONSE = 0x05
    29 FRAME_TYPE_ERROR_RESPONSE = 0x05
       
    30 FRAME_TYPE_TEXT_OUTPUT = 0x06
    30 
    31 
    31 FRAME_TYPES = {
    32 FRAME_TYPES = {
    32     b'command-name': FRAME_TYPE_COMMAND_NAME,
    33     b'command-name': FRAME_TYPE_COMMAND_NAME,
    33     b'command-argument': FRAME_TYPE_COMMAND_ARGUMENT,
    34     b'command-argument': FRAME_TYPE_COMMAND_ARGUMENT,
    34     b'command-data': FRAME_TYPE_COMMAND_DATA,
    35     b'command-data': FRAME_TYPE_COMMAND_DATA,
    35     b'bytes-response': FRAME_TYPE_BYTES_RESPONSE,
    36     b'bytes-response': FRAME_TYPE_BYTES_RESPONSE,
    36     b'error-response': FRAME_TYPE_ERROR_RESPONSE,
    37     b'error-response': FRAME_TYPE_ERROR_RESPONSE,
       
    38     b'text-output': FRAME_TYPE_TEXT_OUTPUT,
    37 }
    39 }
    38 
    40 
    39 FLAG_COMMAND_NAME_EOS = 0x01
    41 FLAG_COMMAND_NAME_EOS = 0x01
    40 FLAG_COMMAND_NAME_HAVE_ARGS = 0x02
    42 FLAG_COMMAND_NAME_HAVE_ARGS = 0x02
    41 FLAG_COMMAND_NAME_HAVE_DATA = 0x04
    43 FLAG_COMMAND_NAME_HAVE_DATA = 0x04
    83     FRAME_TYPE_COMMAND_NAME: FLAGS_COMMAND,
    85     FRAME_TYPE_COMMAND_NAME: FLAGS_COMMAND,
    84     FRAME_TYPE_COMMAND_ARGUMENT: FLAGS_COMMAND_ARGUMENT,
    86     FRAME_TYPE_COMMAND_ARGUMENT: FLAGS_COMMAND_ARGUMENT,
    85     FRAME_TYPE_COMMAND_DATA: FLAGS_COMMAND_DATA,
    87     FRAME_TYPE_COMMAND_DATA: FLAGS_COMMAND_DATA,
    86     FRAME_TYPE_BYTES_RESPONSE: FLAGS_BYTES_RESPONSE,
    88     FRAME_TYPE_BYTES_RESPONSE: FLAGS_BYTES_RESPONSE,
    87     FRAME_TYPE_ERROR_RESPONSE: FLAGS_ERROR_RESPONSE,
    89     FRAME_TYPE_ERROR_RESPONSE: FLAGS_ERROR_RESPONSE,
       
    90     FRAME_TYPE_TEXT_OUTPUT: {},
    88 }
    91 }
    89 
    92 
    90 ARGUMENT_FRAME_HEADER = struct.Struct(r'<HH')
    93 ARGUMENT_FRAME_HEADER = struct.Struct(r'<HH')
    91 
    94 
    92 def makeframe(requestid, frametype, frameflags, payload):
    95 def makeframe(requestid, frametype, frameflags, payload):
   278         flags |= FLAG_ERROR_RESPONSE_PROTOCOL
   281         flags |= FLAG_ERROR_RESPONSE_PROTOCOL
   279     if application:
   282     if application:
   280         flags |= FLAG_ERROR_RESPONSE_APPLICATION
   283         flags |= FLAG_ERROR_RESPONSE_APPLICATION
   281 
   284 
   282     yield makeframe(requestid, FRAME_TYPE_ERROR_RESPONSE, flags, msg)
   285     yield makeframe(requestid, FRAME_TYPE_ERROR_RESPONSE, flags, msg)
       
   286 
       
   287 def createtextoutputframe(requestid, atoms):
       
   288     """Create a text output frame to render text to people.
       
   289 
       
   290     ``atoms`` is a 3-tuple of (formatting string, args, labels).
       
   291 
       
   292     The formatting string contains ``%s`` tokens to be replaced by the
       
   293     corresponding indexed entry in ``args``. ``labels`` is an iterable of
       
   294     formatters to be applied at rendering time. In terms of the ``ui``
       
   295     class, each atom corresponds to a ``ui.write()``.
       
   296     """
       
   297     bytesleft = DEFAULT_MAX_FRAME_SIZE
       
   298     atomchunks = []
       
   299 
       
   300     for (formatting, args, labels) in atoms:
       
   301         if len(args) > 255:
       
   302             raise ValueError('cannot use more than 255 formatting arguments')
       
   303         if len(labels) > 255:
       
   304             raise ValueError('cannot use more than 255 labels')
       
   305 
       
   306         # TODO look for localstr, other types here?
       
   307 
       
   308         if not isinstance(formatting, bytes):
       
   309             raise ValueError('must use bytes formatting strings')
       
   310         for arg in args:
       
   311             if not isinstance(arg, bytes):
       
   312                 raise ValueError('must use bytes for arguments')
       
   313         for label in labels:
       
   314             if not isinstance(label, bytes):
       
   315                 raise ValueError('must use bytes for labels')
       
   316 
       
   317         # Formatting string must be UTF-8.
       
   318         formatting = formatting.decode(r'utf-8', r'replace').encode(r'utf-8')
       
   319 
       
   320         # Arguments must be UTF-8.
       
   321         args = [a.decode(r'utf-8', r'replace').encode(r'utf-8') for a in args]
       
   322 
       
   323         # Labels must be ASCII.
       
   324         labels = [l.decode(r'ascii', r'strict').encode(r'ascii')
       
   325                   for l in labels]
       
   326 
       
   327         if len(formatting) > 65535:
       
   328             raise ValueError('formatting string cannot be longer than 64k')
       
   329 
       
   330         if any(len(a) > 65535 for a in args):
       
   331             raise ValueError('argument string cannot be longer than 64k')
       
   332 
       
   333         if any(len(l) > 255 for l in labels):
       
   334             raise ValueError('label string cannot be longer than 255 bytes')
       
   335 
       
   336         chunks = [
       
   337             struct.pack(r'<H', len(formatting)),
       
   338             struct.pack(r'<BB', len(labels), len(args)),
       
   339             struct.pack(r'<' + r'B' * len(labels), *map(len, labels)),
       
   340             struct.pack(r'<' + r'H' * len(args), *map(len, args)),
       
   341         ]
       
   342         chunks.append(formatting)
       
   343         chunks.extend(labels)
       
   344         chunks.extend(args)
       
   345 
       
   346         atom = b''.join(chunks)
       
   347         atomchunks.append(atom)
       
   348         bytesleft -= len(atom)
       
   349 
       
   350     if bytesleft < 0:
       
   351         raise ValueError('cannot encode data in a single frame')
       
   352 
       
   353     yield makeframe(requestid, FRAME_TYPE_TEXT_OUTPUT, 0, b''.join(atomchunks))
   283 
   354 
   284 class serverreactor(object):
   355 class serverreactor(object):
   285     """Holds state of a server handling frame-based protocol requests.
   356     """Holds state of a server handling frame-based protocol requests.
   286 
   357 
   287     This class is the "brain" of the unified frame-based protocol server
   358     This class is the "brain" of the unified frame-based protocol server