1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.adservices.service.common.bhttp;
18 
19 import static com.android.adservices.service.common.bhttp.BinaryHttpMessage.EMPTY_CONTENT;
20 
21 import android.annotation.NonNull;
22 
23 import com.android.adservices.LoggerFactory;
24 import com.android.internal.annotations.VisibleForTesting;
25 
26 import com.google.auto.value.AutoValue;
27 
28 import java.nio.ByteBuffer;
29 import java.util.Arrays;
30 import java.util.Objects;
31 
32 /** Deserialize the message from a binary representation according to the framing indicator. */
33 @AutoValue
34 public class BinaryHttpMessageDeserializer {
35 
36     /**
37      * Deserialize the message from a binary representation according to the framing indicator.
38      *
39      * @throws IllegalArgumentException when message was truncated in a wrong place or message is
40      *     malformed.
41      * @see <a
42      *     href="https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-padding-and-truncation">Binary
43      *     HTTP Padding And Truncation</a>
44      */
45     @NonNull
deserialize(@onNull final byte[] data)46     public BinaryHttpMessage deserialize(@NonNull final byte[] data) {
47         final BinaryHttpByteArrayReader reader = new BinaryHttpByteArrayReader(data);
48         switch (reader.getFramingIndicatorByte()) {
49             case FramingIndicator.FRAMING_INDICATOR_REQUEST_OF_KNOWN_LENGTH:
50             case FramingIndicator.FRAMING_INDICATOR_RESPONSE_OF_KNOWN_LENGTH:
51                 return deserializeKnownLengthMessage(reader);
52             default:
53                 throw new IllegalArgumentException(
54                         String.format(
55                                 "Invalid framing indicator %d", reader.getFramingIndicatorByte()));
56         }
57     }
58 
59     @NonNull
60     @VisibleForTesting
deserializeKnownLengthRequestControlData( @onNull final BinaryHttpByteArrayReader reader)61     static ControlData deserializeKnownLengthRequestControlData(
62             @NonNull final BinaryHttpByteArrayReader reader) {
63         return RequestControlData.builder()
64                 .setMethod(new String(reader.readNextKnownLengthData().getData()))
65                 .setScheme(new String(reader.readNextKnownLengthData().getData()))
66                 .setAuthority(new String(reader.readNextKnownLengthData().getData()))
67                 .setPath(new String(reader.readNextKnownLengthData().getData()))
68                 .build();
69     }
70 
71     @NonNull
72     @VisibleForTesting
deserializeKnownLengthResponseControlData( @onNull final BinaryHttpByteArrayReader reader)73     static ControlData deserializeKnownLengthResponseControlData(
74             @NonNull final BinaryHttpByteArrayReader reader) {
75         ResponseControlData.Builder builder = ResponseControlData.builder();
76 
77         int statusCode;
78         while (HttpStatusCodeUtil.isInformativeStatusCode(
79                 statusCode = (int) reader.readNextRfc9000Int())) {
80             builder.addInformativeResponse(
81                     InformativeResponse.builder()
82                             .setInformativeStatusCode(statusCode)
83                             .setHeaderFields(deserializeKnownLengthFields(reader))
84                             .build());
85         }
86 
87         builder.setFinalStatusCode(statusCode);
88         return builder.build();
89     }
90 
91     @NonNull
92     @VisibleForTesting
deserializeKnownLengthMessage( @onNull final BinaryHttpByteArrayReader reader)93     static BinaryHttpMessage deserializeKnownLengthMessage(
94             @NonNull final BinaryHttpByteArrayReader reader) {
95         final byte framingIndicator = reader.getFramingIndicatorByte();
96         // For response, the informative responses are included in the control data to simplifying
97         // the code.
98         final ControlData controlData = deserializeKnownLengthControlData(framingIndicator, reader);
99         final Fields headerFields = deserializeKnownLengthFields(reader);
100         byte[] content = EMPTY_CONTENT;
101         if (reader.hasRemainingBytes()) {
102             content = reader.readNextKnownLengthData().getData();
103         }
104         // We don't have trailer support for now. Trailer will be treated as part of padding.
105         int paddingLength = reader.remainingLength();
106         return BinaryHttpMessage.builder()
107                 .setFramingIndicator(framingIndicator)
108                 .setControlData(controlData)
109                 .setHeaderFields(headerFields)
110                 .setContent(content)
111                 .setPaddingLength(paddingLength)
112                 .build();
113     }
114 
115     @NonNull
deserializeKnownLengthControlData( final byte framingIndicator, @NonNull final BinaryHttpByteArrayReader reader)116     private static ControlData deserializeKnownLengthControlData(
117             final byte framingIndicator, @NonNull final BinaryHttpByteArrayReader reader) {
118         switch (framingIndicator) {
119             case FramingIndicator.FRAMING_INDICATOR_REQUEST_OF_KNOWN_LENGTH:
120                 return deserializeKnownLengthRequestControlData(reader);
121             case FramingIndicator.FRAMING_INDICATOR_RESPONSE_OF_KNOWN_LENGTH:
122                 return deserializeKnownLengthResponseControlData(reader);
123             default:
124                 throw new IllegalArgumentException(
125                         String.format(
126                                 "Invalid framing indicator %d", reader.getFramingIndicatorByte()));
127         }
128     }
129 
130     @NonNull
deserializeKnownLengthFields(@onNull final BinaryHttpByteArrayReader reader)131     static Fields deserializeKnownLengthFields(@NonNull final BinaryHttpByteArrayReader reader) {
132         Fields.Builder builder = Fields.builder();
133         BinaryHttpMessageDeserializer.BinaryHttpByteArrayReader subReader =
134                 reader.readNextKnownLengthData();
135         while (subReader.hasRemainingBytes()) {
136             builder.appendField(
137                     new String(subReader.readNextKnownLengthData().getData()),
138                     new String(subReader.readNextKnownLengthData().getData()));
139         }
140         return builder.build();
141     }
142 
143     /** Helper class of separating the block of data in a binary HTTP message. */
144     @VisibleForTesting
145     static class BinaryHttpByteArrayReader {
146         private static final LoggerFactory.Logger LOGGER = LoggerFactory.getFledgeLogger();
147         @NonNull private final byte[] mData;
148         private final int mDataBlockEndIndex;
149         private int mNextIndex;
150 
151         /**
152          * Initial the reader taking a Binary HTTP message byte array.
153          *
154          * <p>The reader starts at index 1 since the first byte of the array is the framing
155          * indicator.
156          */
BinaryHttpByteArrayReader(@onNull final byte[] data)157         BinaryHttpByteArrayReader(@NonNull final byte[] data) {
158             this(data, 1, data.length);
159         }
160 
BinaryHttpByteArrayReader( @onNull final byte[] data, final int nextIndex, final int dataBlockEndIndex)161         private BinaryHttpByteArrayReader(
162                 @NonNull final byte[] data, final int nextIndex, final int dataBlockEndIndex) {
163             Objects.requireNonNull(data);
164             mData = data;
165             mDataBlockEndIndex = dataBlockEndIndex;
166             mNextIndex = nextIndex;
167         }
168 
169         /**
170          * Returns the next section of known length data.
171          *
172          * <p>First read the length indicator, then return the Reader for it's corresponding data
173          * block.
174          *
175          * <p>Major use case is in the field reading. Fields are represents in [total field length]
176          * [[name length][name][value length][value]]*n. We need to return a sub reader for the data
177          * of fields.
178          */
179         @NonNull
readNextKnownLengthData()180         BinaryHttpByteArrayReader readNextKnownLengthData() {
181             int start = mNextIndex;
182             int dataLength;
183             try {
184                 dataLength = (int) readNextRfc9000Int();
185             } catch (IllegalArgumentException e) {
186                 LOGGER.e(
187                         "No sufficient bytes for data length at index %d, out of boundary at %d",
188                         start, mDataBlockEndIndex);
189                 throw new IllegalArgumentException("No sufficient bytes can be read.", e);
190             }
191             int newDataBlockEndIndex = mNextIndex + dataLength;
192             if (newDataBlockEndIndex > mDataBlockEndIndex) {
193                 LOGGER.e(
194                         "No sufficient bytes for data at %d, actual end %d, requested end %d",
195                         mNextIndex, mDataBlockEndIndex, newDataBlockEndIndex);
196                 throw new IllegalArgumentException("No sufficient bytes can be read.");
197             }
198             BinaryHttpByteArrayReader subReader =
199                     new BinaryHttpByteArrayReader(mData, mNextIndex, newDataBlockEndIndex);
200             mNextIndex += dataLength;
201             return subReader;
202         }
203 
204         @NonNull
getData()205         byte[] getData() {
206             return Arrays.copyOfRange(mData, mNextIndex, mDataBlockEndIndex);
207         }
208 
hasRemainingBytes()209         boolean hasRemainingBytes() {
210             return mNextIndex < mDataBlockEndIndex;
211         }
212 
remainingLength()213         int remainingLength() {
214             return mDataBlockEndIndex - mNextIndex;
215         }
216 
getFramingIndicatorByte()217         byte getFramingIndicatorByte() {
218             return mData[0];
219         }
220 
221         /**
222          * Returns next FRC 9000 integer.
223          *
224          * @see <a
225          *     href="https://datatracker.ietf.org/doc/html/rfc9000#name-variable-length-integer-enc">FRC
226          *     9000 Variable Length Integer</a>
227          */
readNextRfc9000Int()228         long readNextRfc9000Int() {
229             throwForNotEnoughBytesForInt(1);
230             long result;
231             // The highest 2 bit of starting byte indicates the length of the integer
232             // representation:
233             // 11 indicates 8 bytes;
234             // 10 indicates 4 bytes;
235             // 01 indicates 2 bytes;
236             // 00 indicates 1 byte.
237             switch (mData[mNextIndex] & 0b11000000) {
238                 case 0b11000000:
239                     // Leading 0b11...... is 8 byte encoding
240                     throwForNotEnoughBytesForInt(8);
241                     result =
242                             ByteBuffer.allocate(8)
243                                     .put((byte) (mData[mNextIndex] & 0b00111111))
244                                     .put(mData, mNextIndex + 1, 7)
245                                     .getLong(0);
246                     mNextIndex += 8;
247                     return result;
248                 case 0b10000000:
249                     // Leading 0b10...... is 4 byte encoding
250                     throwForNotEnoughBytesForInt(4);
251                     result =
252                             ByteBuffer.allocate(8)
253                                     .put(new byte[] {0, 0, 0, 0})
254                                     .put((byte) (mData[mNextIndex] & 0b00111111))
255                                     .put(mData, mNextIndex + 1, 3)
256                                     .getLong(0);
257                     mNextIndex += 4;
258                     return result;
259                 case 0b01000000:
260                     // Leading 0b01...... is 2 byte encoding
261                     throwForNotEnoughBytesForInt(2);
262                     result =
263                             ByteBuffer.allocate(8)
264                                     .put(new byte[] {0, 0, 0, 0, 0, 0})
265                                     .put((byte) (mData[mNextIndex] & 0b00111111))
266                                     .put(mData[mNextIndex + 1])
267                                     .getLong(0);
268                     mNextIndex += 2;
269                     return result;
270                 case 0b00000000:
271                 default:
272                     // Leading 0b00...... is 1 byte encoding
273                     result = mData[mNextIndex++];
274                     return result;
275             }
276         }
277 
throwForNotEnoughBytesForInt(final int requiredLength)278         private void throwForNotEnoughBytesForInt(final int requiredLength) {
279             int remainingLength = remainingLength();
280             if (remainingLength < requiredLength) {
281                 LOGGER.e(
282                         "Not enough data to be read as FRC 9000 integer, first byte at %d of "
283                                 + "(%s), needed length %d, remaining length %d.",
284                         mNextIndex,
285                         remainingLength > 0
286                                 ? Integer.toString(mData[mNextIndex])
287                                 : "already out of bound",
288                         requiredLength,
289                         remainingLength);
290                 throw new IllegalArgumentException(
291                         "Not enough data to be read as FRC 9000 integer.");
292             }
293         }
294     }
295 }
296