forked from getsentry/sentry-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEnvelopeReader.java
More file actions
146 lines (125 loc) · 5.3 KB
/
EnvelopeReader.java
File metadata and controls
146 lines (125 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package io.sentry;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ApiStatus.Internal
public final class EnvelopeReader implements IEnvelopeReader {
@SuppressWarnings("CharsetObjectCanBeUsed")
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final @NotNull ISerializer serializer;
public EnvelopeReader(@NotNull ISerializer serializer) {
this.serializer = serializer;
}
public @Override @Nullable SentryEnvelope read(final @NotNull InputStream stream)
throws IOException {
byte[] buffer = new byte[1024];
int currentLength;
int streamOffset = 0;
// Offset of the line break defining the end of the envelope header
int envelopeEndHeaderOffset = -1;
try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
while ((currentLength = stream.read(buffer)) > 0) {
for (int i = 0; envelopeEndHeaderOffset == -1 && i < currentLength; i++) {
if (buffer[i] == '\n') {
envelopeEndHeaderOffset = streamOffset + i;
break;
}
}
outputStream.write(buffer, 0, currentLength);
streamOffset += currentLength;
}
// TODO: Work on the stream instead reading to the whole thing and allocating this array
byte[] envelopeBytes = outputStream.toByteArray();
if (envelopeBytes.length == 0) {
throw new IllegalArgumentException("Empty stream.");
}
if (envelopeEndHeaderOffset == -1) {
throw new IllegalArgumentException("Envelope contains no header.");
}
SentryEnvelopeHeader header =
deserializeEnvelopeHeader(envelopeBytes, 0, envelopeEndHeaderOffset);
if (header == null) {
throw new IllegalArgumentException("Envelope header is null.");
}
int itemHeaderStartOffset = envelopeEndHeaderOffset + 1;
int payloadEndOffsetExclusive;
List<SentryEnvelopeItem> items = new ArrayList<>();
do {
int lineBreakIndex = -1;
// Look from startHeaderOffset until line break to find next header
for (int i = itemHeaderStartOffset; i < envelopeBytes.length; i++) {
if (envelopeBytes[i] == '\n') {
lineBreakIndex = i;
break;
}
}
if (lineBreakIndex == -1) {
throw new IllegalArgumentException(
"Invalid envelope. Item at index '"
+ items.size()
+ "'. "
+ "has no header delimiter.");
}
SentryEnvelopeItemHeader itemHeader =
deserializeEnvelopeItemHeader(
envelopeBytes, itemHeaderStartOffset, lineBreakIndex - itemHeaderStartOffset);
if (itemHeader == null || itemHeader.getLength() <= 0) {
throw new IllegalArgumentException(
"Item header at index '" + items.size() + "' is null or empty.");
}
payloadEndOffsetExclusive = lineBreakIndex + itemHeader.getLength() + 1;
if (payloadEndOffsetExclusive > envelopeBytes.length) {
throw new IllegalArgumentException(
"Invalid length for item at index '"
+ items.size()
+ "'. "
+ "Item is '"
+ payloadEndOffsetExclusive
+ "' bytes. There are '"
+ envelopeBytes.length
+ "' in the buffer.");
}
// if 'to' parameter overflows, copyOfRange is happy to pretend nothing happened. Bound need
// checking.
byte[] envelopeItemBytes =
Arrays.copyOfRange(
envelopeBytes, lineBreakIndex + 1, payloadEndOffsetExclusive /* to is exclusive */);
SentryEnvelopeItem item = new SentryEnvelopeItem(itemHeader, envelopeItemBytes);
items.add(item);
if (payloadEndOffsetExclusive == envelopeBytes.length) {
// End of envelope
break;
} else if (payloadEndOffsetExclusive + 1 == envelopeBytes.length) {
// Envelope items can be closed with a final line break
if (envelopeBytes[payloadEndOffsetExclusive] == '\n') {
break;
} else {
throw new IllegalArgumentException("Envelope has invalid data following an item.");
}
}
itemHeaderStartOffset = payloadEndOffsetExclusive + 1; // Skip over delimiter
} while (true);
return new SentryEnvelope(header, items);
}
}
private @Nullable SentryEnvelopeHeader deserializeEnvelopeHeader(
final @NotNull byte[] buffer, int offset, int length) {
String json = new String(buffer, offset, length, UTF_8);
StringReader reader = new StringReader(json);
return serializer.deserialize(reader, SentryEnvelopeHeader.class);
}
private @Nullable SentryEnvelopeItemHeader deserializeEnvelopeItemHeader(
final @NotNull byte[] buffer, int offset, int length) {
String json = new String(buffer, offset, length, UTF_8);
StringReader reader = new StringReader(json);
return serializer.deserialize(reader, SentryEnvelopeItemHeader.class);
}
}