handleMessage method
- 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.');
}
}
}