mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 16:19:33 +01:00
Add better logging for ovesized messages.
This commit is contained in:
committed by
Alex Hart
parent
caa743aba2
commit
ae04749336
@@ -8,6 +8,7 @@ package org.whispersystems.signalservice.api;
|
||||
import org.signal.core.models.ServiceId;
|
||||
import org.signal.core.models.ServiceId.PNI;
|
||||
import org.signal.core.util.Base64;
|
||||
import org.signal.core.util.ProtoUtil;
|
||||
import org.signal.core.util.UuidUtil;
|
||||
import org.signal.libsignal.metadata.certificate.SenderCertificate;
|
||||
import org.signal.libsignal.protocol.IdentityKey;
|
||||
@@ -2981,6 +2982,7 @@ public class SignalServiceMessageSender {
|
||||
} else {
|
||||
message = buildContentTooLargeBreadcrumbs(content.getContent().get());
|
||||
}
|
||||
Log.w(TAG, "About to crash for exceeding max envelope size (" + size + " > " + maxEnvelopeSize + ")\n" + message);
|
||||
throw new ContentTooLargeException(size, message);
|
||||
}
|
||||
}
|
||||
@@ -2996,104 +2998,7 @@ public class SignalServiceMessageSender {
|
||||
}
|
||||
|
||||
private String buildContentTooLargeBreadcrumbs(Content content) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
|
||||
if (content.dataMessage != null) {
|
||||
message.append("Data message;");
|
||||
if (content.dataMessage.body != null && !content.dataMessage.body.isEmpty()) {
|
||||
message.append("Body(").append(content.dataMessage.body.length()).append(");");
|
||||
}
|
||||
if (content.dataMessage.groupV2 != null) {
|
||||
if (content.dataMessage.groupV2.groupChange != null) {
|
||||
message.append("GroupV2Change(").append(content.dataMessage.groupV2.groupChange.size()).append(");");
|
||||
} else {
|
||||
message.append("GroupV2NoChange;");
|
||||
}
|
||||
}
|
||||
if (content.dataMessage.giftBadge != null) {
|
||||
message.append("GiftBadge;");
|
||||
}
|
||||
if (content.dataMessage.pollCreate != null) {
|
||||
message.append("PollCreate;");
|
||||
}
|
||||
if (content.dataMessage.pollTerminate != null) {
|
||||
message.append("PollTerminate;");
|
||||
}
|
||||
if (content.dataMessage.pollVote != null) {
|
||||
message.append("PollVote;");
|
||||
}
|
||||
if (content.dataMessage.pinMessage != null) {
|
||||
message.append("PinMessage;");
|
||||
}
|
||||
if (content.dataMessage.unpinMessage != null) {
|
||||
message.append("UnpinMessage;");
|
||||
}
|
||||
if (content.dataMessage.reaction != null) {
|
||||
message.append("Reaction;");
|
||||
}
|
||||
if (content.dataMessage.profileKey != null && content.dataMessage.profileKey.size() > 0) {
|
||||
message.append("ProfileKey(").append(content.dataMessage.profileKey.size()).append(");");
|
||||
}
|
||||
if (content.dataMessage.payment != null) {
|
||||
message.append("Payment;");
|
||||
}
|
||||
if (!content.dataMessage.attachments.isEmpty()) {
|
||||
message.append("Attachments(").append(content.dataMessage.attachments.size()).append(");");
|
||||
}
|
||||
if (!content.dataMessage.contact.isEmpty()) {
|
||||
message.append("Contacts(").append(content.dataMessage.contact.size()).append(");");
|
||||
}
|
||||
if (!content.dataMessage.bodyRanges.isEmpty()) {
|
||||
message.append("Ranges(").append(content.dataMessage.bodyRanges.size()).append(");");
|
||||
}
|
||||
if (content.dataMessage.quote != null) {
|
||||
if (content.dataMessage.quote.text != null) {
|
||||
message.append("Quote(").append(content.dataMessage.quote.text.length()).append(");");
|
||||
} else {
|
||||
message.append("Quote(No text);");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (content.syncMessage != null) {
|
||||
message.append("Sync message;");
|
||||
|
||||
if (content.syncMessage.sent != null) {
|
||||
if (content.syncMessage.sent.storyMessage != null) {
|
||||
message.append("StoryMessage(").append(content.syncMessage.sent.storyMessageRecipients.size()).append(");");
|
||||
}
|
||||
if (!content.syncMessage.sent.storyMessageRecipients.isEmpty()) {
|
||||
message.append("StoryRecipients(").append(content.syncMessage.sent.storyMessageRecipients.size()).append(");");
|
||||
}
|
||||
if (content.syncMessage.blocked != null) {
|
||||
message.append("Blocked-AciString(").append(content.syncMessage.blocked.acis.size()).append(");");
|
||||
message.append("Blocked-AciBinary(").append(content.syncMessage.blocked.acisBinary.size()).append(");");
|
||||
message.append("Blocked-GroupIds(").append(content.syncMessage.blocked.groupIds.size()).append(");");
|
||||
message.append("Blocked-Numbers(").append(content.syncMessage.blocked.numbers.size()).append(");");
|
||||
}
|
||||
if (content.syncMessage.outgoingPayment != null) {
|
||||
message.append("OutgoingPayment");
|
||||
}
|
||||
if (content.syncMessage.deleteForMe != null) {
|
||||
message.append("DeleteForMe-Messages(").append(content.syncMessage.deleteForMe.messageDeletes.size()).append(");");
|
||||
message.append("DeleteForMe-Attachments(").append(content.syncMessage.deleteForMe.attachmentDeletes.size()).append(");");
|
||||
message.append("DeleteForMe-Conversations(").append(content.syncMessage.deleteForMe.conversationDeletes.size()).append(");");
|
||||
}
|
||||
if (!content.syncMessage.read.isEmpty()) {
|
||||
message.append("Read(").append(content.syncMessage.read.size()).append(");");
|
||||
}
|
||||
if (!content.syncMessage.viewed.isEmpty()) {
|
||||
message.append("Viewed(").append(content.syncMessage.read.size()).append(");");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (content.receiptMessage != null) {
|
||||
message.append("ReceiptMessage(").append(content.receiptMessage.timestamp.size()).append(");");
|
||||
message.append("ReceiptMessage(").append(content.receiptMessage.type.getValue()).append(");");
|
||||
}
|
||||
|
||||
return message.toString();
|
||||
return ProtoUtil.buildSizeTree(content, "Content");
|
||||
}
|
||||
|
||||
public interface EventListener {
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.core.util
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.doesNotContain
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.startsWith
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.junit.Test
|
||||
import org.whispersystems.signalservice.internal.push.BodyRange
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.GroupContextV2
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage
|
||||
|
||||
class BuildSizeTreeTest {
|
||||
|
||||
@Test
|
||||
fun `empty message has zero encoded size`() {
|
||||
val msg = DataMessage()
|
||||
val tree = msg.buildSizeTree("DataMessage")
|
||||
assertThat(tree).isEqualTo("DataMessage(0)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `string field shows utf8 byte size`() {
|
||||
val msg = DataMessage(body = "hello")
|
||||
val tree = msg.buildSizeTree("DataMessage")
|
||||
|
||||
assertThat(tree).startsWith("DataMessage(")
|
||||
assertThat(tree).contains("\n body(5)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multi-byte utf8 string shows correct byte size`() {
|
||||
val msg = DataMessage(body = "\uD83D\uDE00") // emoji, 4 bytes in UTF-8
|
||||
val tree = msg.buildSizeTree("DataMessage")
|
||||
|
||||
assertThat(tree).contains("body(4)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ByteString field shows raw byte size`() {
|
||||
val bytes = ByteArray(100).toByteString()
|
||||
val msg = DataMessage(profileKey = bytes)
|
||||
val tree = msg.buildSizeTree("DataMessage")
|
||||
|
||||
assertThat(tree).contains("\n profileKey(100)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `null and empty fields are omitted`() {
|
||||
val msg = DataMessage(body = "hi")
|
||||
val tree = msg.buildSizeTree("DataMessage")
|
||||
|
||||
assertThat(tree).doesNotContain("profileKey")
|
||||
assertThat(tree).doesNotContain("groupV2")
|
||||
assertThat(tree).doesNotContain("attachments")
|
||||
assertThat(tree).doesNotContain("bodyRanges")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nested message field shows indented sub-tree`() {
|
||||
val msg = Content(
|
||||
dataMessage = DataMessage(body = "test")
|
||||
)
|
||||
val tree = msg.buildSizeTree("Content")
|
||||
|
||||
assertThat(tree).startsWith("Content(")
|
||||
assertThat(tree).contains("\n dataMessage(")
|
||||
assertThat(tree).contains("\n body(4)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeated message field shows count and total size with elements`() {
|
||||
val ranges = listOf(
|
||||
BodyRange(start = 0, length = 5),
|
||||
BodyRange(start = 10, length = 3)
|
||||
)
|
||||
val msg = DataMessage(bodyRanges = ranges)
|
||||
val tree = msg.buildSizeTree("DataMessage")
|
||||
|
||||
assertThat(tree).contains("bodyRanges[2](")
|
||||
assertThat(tree).contains("bodyRanges[0](")
|
||||
assertThat(tree).contains("bodyRanges[1](")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deeply nested structure shows correct indentation`() {
|
||||
val groupChange = ByteArray(50).toByteString()
|
||||
val masterKey = ByteArray(32).toByteString()
|
||||
val msg = Content(
|
||||
dataMessage = DataMessage(
|
||||
body = "hi",
|
||||
groupV2 = GroupContextV2(
|
||||
masterKey = masterKey,
|
||||
groupChange = groupChange,
|
||||
revision = 5
|
||||
)
|
||||
)
|
||||
)
|
||||
val tree = msg.buildSizeTree("Content")
|
||||
|
||||
// depth 0
|
||||
assertThat(tree).startsWith("Content(")
|
||||
// depth 1
|
||||
assertThat(tree).contains("\n dataMessage(")
|
||||
// depth 2
|
||||
assertThat(tree).contains("\n body(2)")
|
||||
assertThat(tree).contains("\n groupV2(")
|
||||
// depth 3
|
||||
assertThat(tree).contains("\n masterKey(32)")
|
||||
assertThat(tree).contains("\n groupChange(50)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `encoded sizes match wire adapter`() {
|
||||
val msg = Content(
|
||||
dataMessage = DataMessage(body = "hello world")
|
||||
)
|
||||
val tree = msg.buildSizeTree("Content")
|
||||
|
||||
val contentSize = Content.ADAPTER.encodedSize(msg)
|
||||
val dataMessageSize = DataMessage.ADAPTER.encodedSize(msg.dataMessage!!)
|
||||
|
||||
assertThat(tree).startsWith("Content($contentSize)")
|
||||
assertThat(tree).contains("\n dataMessage($dataMessageSize)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multiple top-level fields are all included`() {
|
||||
val msg = Content(
|
||||
dataMessage = DataMessage(body = "hi"),
|
||||
syncMessage = SyncMessage()
|
||||
)
|
||||
val tree = msg.buildSizeTree("Content")
|
||||
|
||||
assertThat(tree).contains("dataMessage(")
|
||||
assertThat(tree).contains("syncMessage(")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `custom root name is used`() {
|
||||
val msg = DataMessage(body = "x")
|
||||
val tree = msg.buildSizeTree("MyCustomName")
|
||||
|
||||
assertThat(tree).startsWith("MyCustomName(")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user