handleMessage method

void handleMessage(
  1. ByteData data
)

Handle a control message.

This is intended to be called by the platform messages dispatcher, forwarding messages from plugins to the kControlChannelName channel.

Messages use the StandardMethodCodec format. There are two methods supported: resize and overflow. The resize method changes the size of the buffer, and the overflow method controls whether overflow is expected or not.

resize

The resize method takes as its argument a list with two values, first the channel name (a UTF-8 string less than 254 bytes long and not containing any null bytes), and second the allowed size of the channel buffer (an integer between 0 and 2147483647).

Upon receiving the message, the channel's buffer is resized. If necessary, messages are silently discarded to ensure the buffer is no bigger than specified.

For historical reasons, this message can also be sent using a bespoke format consisting of a UTF-8-encoded string with three parts separated from each other by U+000D CARRIAGE RETURN (CR) characters, the three parts being the string resize, the string giving the channel name, and then the string giving the decimal serialization of the new channel buffer size. For example: resize\rchannel\r1

overflow

The overflow method takes as its argument a list with two values, first the channel name (a UTF-8 string less than 254 bytes long and not containing any null bytes), and second a boolean which is true if overflow is expected and false if it is not.

This sets a flag on the channel in debug mode. In release mode the message is silently ignored. The flag indicates whether overflow is expected on this channel. When the flag is set, messages are discarded silently. When the flag is cleared (the default), any overflow on the channel causes a message to be printed to the console, warning that a message was lost.

Implementation

void handleMessage(ByteData data) {
  // We hard-code the deserialization here because the StandardMethodCodec class
  // is part of the framework, not dart:ui.
  final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
  if (bytes[0] == 0x07) { // 7 = value code for string
    final int methodNameLength = bytes[1];
    if (methodNameLength >= 254) { // lengths greater than 253 have more elaborate encoding
      throw Exception('Unrecognized message sent to $kControlChannelName (method name too long)');
    }
    int index = 2; // where we are in reading the bytes
    final String methodName = utf8.decode(bytes.sublist(index, index + methodNameLength));
    index += methodNameLength;
    switch (methodName) {
      case 'resize':
        if (bytes[index] != 0x0C) { // 12 = value code for list
          throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)");
        }
        index += 1;
        if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2.
          throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)");
        }
        index += 1;
        if (bytes[index] != 0x07) { // 7 = value code for string
          throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (first argument must be a string)");
        }
        index += 1;
        final int channelNameLength = bytes[index];
        if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding
          throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must be less than 254 characters long)");
        }
        index += 1;
        final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength));
        if (channelName.contains('\u0000')) {
          throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must not contain any null bytes)");
        }
        index += channelNameLength;
        if (bytes[index] != 0x03) { // 3 = value code for uint32
          throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)");
        }
        index += 1;
        resize(channelName, data.getUint32(index, Endian.host));
      case 'overflow':
        if (bytes[index] != 0x0C) { // 12 = value code for list
          throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)");
        }
        index += 1;
        if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2.
          throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)");
        }
        index += 1;
        if (bytes[index] != 0x07) { // 7 = value code for string
          throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (first argument must be a string)");
        }
        index += 1;
        final int channelNameLength = bytes[index];
        if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding
          throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (channel name must be less than 254 characters long)");
        }
        index += 1;
        final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength));
        index += channelNameLength;
        if (bytes[index] != 0x01 && bytes[index] != 0x02) { // 1 = value code for true, 2 = value code for false
          throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (second argument must be a boolean)");
        }
        allowOverflow(channelName, bytes[index] == 0x01);
      default:
        throw Exception("Unrecognized method '$methodName' sent to $kControlChannelName");
    }
  } else {
    final List<String> parts = utf8.decode(bytes).split('\r');
    if (parts.length == 1 + /*arity=*/2 && parts[0] == 'resize') {
      resize(parts[1], int.parse(parts[2]));
    } else {
      // If the message couldn't be decoded as UTF-8, a FormatException will
      // have been thrown by utf8.decode() above.
      throw Exception('Unrecognized message $parts sent to $kControlChannelName.');
    }
  }
}