1 /*
2 * Copyright 2015 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package io.netty.handler.codec.protobuf;
17
18 import com.google.protobuf.CodedInputStream;
19 import com.google.protobuf.nano.CodedInputByteBufferNano;
20 import io.netty.buffer.ByteBuf;
21 import io.netty.channel.ChannelHandlerContext;
22 import io.netty.handler.codec.ByteToMessageDecoder;
23 import io.netty.handler.codec.CorruptedFrameException;
24 import io.netty.handler.codec.TooLongFrameException;
25
26 import java.util.List;
27
28 import static io.netty.util.internal.ObjectUtil.checkPositive;
29
30 /**
31 * A decoder that splits the received {@link ByteBuf}s dynamically by the
32 * value of the Google Protocol Buffers
33 * <a href="https://developers.google.com/protocol-buffers/docs/encoding#varints">Base
34 * 128 Varints</a> integer length field in the message. For example:
35 * <pre>
36 * BEFORE DECODE (302 bytes) AFTER DECODE (300 bytes)
37 * +--------+---------------+ +---------------+
38 * | Length | Protobuf Data |----->| Protobuf Data |
39 * | 0xAC02 | (300 bytes) | | (300 bytes) |
40 * +--------+---------------+ +---------------+
41 * </pre>
42 *
43 * @see CodedInputStream
44 * @see CodedInputByteBufferNano
45 */
46 public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder {
47
48 private final int maxFrameLength;
49 private long bytesToDiscard;
50
51 /**
52 * Creates a new instance with no frame length limit.
53 */
54 public ProtobufVarint32FrameDecoder() {
55 this(Integer.MAX_VALUE);
56 }
57
58 /**
59 * Creates a new instance with the specified maximum frame length.
60 *
61 * @param maxFrameLength the maximum length of the frame.
62 * If the length exceeds this value,
63 * {@link TooLongFrameException} will be thrown.
64 */
65 public ProtobufVarint32FrameDecoder(int maxFrameLength) {
66 this.maxFrameLength = checkPositive(maxFrameLength, "maxFrameLength");
67 }
68
69 @Override
70 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
71 throws Exception {
72 if (bytesToDiscard > 0) {
73 int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
74 in.skipBytes(localBytesToDiscard);
75 bytesToDiscard -= localBytesToDiscard;
76 return;
77 }
78
79 in.markReaderIndex();
80 int preIndex = in.readerIndex();
81 int length = readRawVarint32(in);
82 if (preIndex == in.readerIndex()) {
83 return;
84 }
85 if (length < 0) {
86 throw new CorruptedFrameException("negative length: " + length);
87 }
88
89 if (length > maxFrameLength) {
90 long discard = length - in.readableBytes();
91 if (discard <= 0) {
92 in.skipBytes(length);
93 } else {
94 bytesToDiscard = discard;
95 in.skipBytes(in.readableBytes());
96 }
97 throw new TooLongFrameException(
98 "Frame length exceeds " + maxFrameLength
99 + ": " + length);
100 }
101
102 if (in.readableBytes() < length) {
103 in.resetReaderIndex();
104 } else {
105 out.add(in.readRetainedSlice(length));
106 }
107 }
108
109 /**
110 * Reads variable length 32bit int from buffer
111 *
112 * @return decoded int if buffers readerIndex has been forwarded else nonsense value
113 */
114 static int readRawVarint32(ByteBuf buffer) {
115 if (buffer.readableBytes() < 4) {
116 return readRawVarint24(buffer);
117 }
118 int wholeOrMore = buffer.getIntLE(buffer.readerIndex());
119 int firstOneOnStop = ~wholeOrMore & 0x80808080;
120 if (firstOneOnStop == 0) {
121 return readRawVarint40(buffer, wholeOrMore);
122 }
123 int bitsToKeep = Integer.numberOfTrailingZeros(firstOneOnStop) + 1;
124 buffer.skipBytes(bitsToKeep >> 3);
125 int thisVarintMask = firstOneOnStop ^ (firstOneOnStop - 1);
126 int wholeWithContinuations = wholeOrMore & thisVarintMask;
127 // mix them up as per varint spec while dropping the continuation bits:
128 // 0x7F007F isolate the first byte and the third byte dropping the continuation bits
129 // 0x7F007F00 isolate the second byte and the fourth byte dropping the continuation bits
130 // the second and fourth byte are shifted to the right by 1, filling the gaps left by the first and third byte
131 // it means that the first and second bytes now occupy the first 14 bits (7 bits each)
132 // and the third and fourth bytes occupy the next 14 bits (7 bits each), with a gap between the 2s of 2 bytes
133 // and another gap of 2 bytes after the forth and third.
134 wholeWithContinuations = (wholeWithContinuations & 0x7F007F) | ((wholeWithContinuations & 0x7F007F00) >> 1);
135 // 0x3FFF isolate the first 14 bits i.e. the first and second bytes
136 // 0x3FFF0000 isolate the next 14 bits i.e. the third and forth bytes
137 // the third and forth bytes are shifted to the right by 2, filling the gaps left by the first and second bytes
138 return (wholeWithContinuations & 0x3FFF) | ((wholeWithContinuations & 0x3FFF0000) >> 2);
139 }
140
141 private static int readRawVarint40(ByteBuf buffer, int wholeOrMore) {
142 if (buffer.readableBytes() == 4) {
143 // The first 4 bytes all have the continuation bit set, but the 5th byte did not arrive yet.
144 // Leave the readerIndex untouched so the caller waits for more data.
145 return 0;
146 }
147 byte lastByte = buffer.getByte(buffer.readerIndex() + 4);
148 if (lastByte < 0) {
149 throw new CorruptedFrameException("malformed varint.");
150 }
151 buffer.skipBytes(5);
152 // add it to wholeOrMore
153 return wholeOrMore & 0x7F |
154 (((wholeOrMore >> 8) & 0x7F) << 7) |
155 (((wholeOrMore >> 16) & 0x7F) << 14) |
156 (((wholeOrMore >> 24) & 0x7F) << 21) |
157 (lastByte << 28);
158 }
159
160 private static int readRawVarint24(ByteBuf buffer) {
161 if (!buffer.isReadable()) {
162 return 0;
163 }
164 buffer.markReaderIndex();
165
166 byte tmp = buffer.readByte();
167 if (tmp >= 0) {
168 return tmp;
169 }
170 int result = tmp & 127;
171 if (!buffer.isReadable()) {
172 buffer.resetReaderIndex();
173 return 0;
174 }
175 if ((tmp = buffer.readByte()) >= 0) {
176 return result | tmp << 7;
177 }
178 result |= (tmp & 127) << 7;
179 if (!buffer.isReadable()) {
180 buffer.resetReaderIndex();
181 return 0;
182 }
183 if ((tmp = buffer.readByte()) >= 0) {
184 return result | tmp << 14;
185 }
186 // All 3 readable bytes have the continuation bit set, which means at least one more byte is needed to
187 // complete the varint. We only end up here if less than 4 bytes were readable, so wait for more data.
188 buffer.resetReaderIndex();
189 return 0;
190 }
191 }