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.Frc9000VariableLengthIntegerUtil.toFrc9000Int;
20 
21 import android.annotation.NonNull;
22 
23 import com.google.auto.value.AutoValue;
24 import com.google.common.collect.ImmutableList;
25 
26 import java.util.Objects;
27 
28 /**
29  * Represents a list of optional {@link InformativeResponse}s and a final response control data.
30  *
31  * <p>If the response control data includes an informational status code (that is, a value between
32  * 100 and 199 inclusive), the control data is followed by a header section (encoded with known- or
33  * indeterminate- length according to the framing indicator) and another block of control data .
34  * This pattern repeats until the control data contains a final status code (200 to 599 inclusive).
35  *
36  * <p>The control data for a response message consists of the status code. The status code is
37  * encoded as a variable length integer, not a length-prefixed decimal string.
38  *
39  * @see <a
40  *     href="https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-response-control-data">Binary
41  *     HTTP Response Control Data</a>
42  */
43 @AutoValue
44 public abstract class ResponseControlData extends ControlData {
45     private static final int FINAL_STATUS_CODE_SECTION_COUNT = 1;
46 
47     /** Returns the final status code of the response. */
getFinalStatusCode()48     public abstract int getFinalStatusCode();
49 
50     /** Returns the informative responses of the response. */
51     @NonNull
getInformativeResponses()52     public abstract ImmutableList<InformativeResponse> getInformativeResponses();
53 
54     /**
55      * {@inheritDoc}
56      *
57      * @return [[informative status code][header fields sections]*n]*n[final status code]
58      * @see Fields#knownLengthSerialize()
59      */
60     @Override
61     @NonNull
knownLengthSerialize()62     byte[][] knownLengthSerialize() {
63         int totalLength =
64                 FINAL_STATUS_CODE_SECTION_COUNT
65                         + getInformativeResponses().stream()
66                                 .mapToInt(
67                                         InformativeResponse::getKnownLengthSerializedSectionsCount)
68                                 .sum();
69         byte[][] result = new byte[totalLength][];
70         int pos = 0;
71 
72         for (InformativeResponse informativeResponse : getInformativeResponses()) {
73             byte[][] subSections = informativeResponse.knownLengthSerialize();
74             System.arraycopy(subSections, 0, result, pos, subSections.length);
75             pos += subSections.length;
76         }
77 
78         result[pos] = toFrc9000Int(getFinalStatusCode());
79 
80         return result;
81     }
82 
83     @Override
getKnownLengthSerializedSectionsCount()84     int getKnownLengthSerializedSectionsCount() {
85         return getInformativeResponses().stream()
86                         .mapToInt(InformativeResponse::getKnownLengthSerializedSectionsCount)
87                         .sum()
88                 + FINAL_STATUS_CODE_SECTION_COUNT;
89     }
90 
91     /** Get a builder for response control data. */
92     @NonNull
builder()93     public static Builder builder() {
94         return new AutoValue_ResponseControlData.Builder();
95     }
96 
97     /** Builder for {@link ResponseControlData}. */
98     @AutoValue.Builder
99     public abstract static class Builder {
100         /** Sets the final status code of the response. */
setFinalStatusCode(int value)101         public abstract Builder setFinalStatusCode(int value);
102 
103         @NonNull
informativeResponsesBuilder()104         abstract ImmutableList.Builder<InformativeResponse> informativeResponsesBuilder();
105 
106         /** Append an informative response to the response. */
addInformativeResponse( @onNull final InformativeResponse informativeResponse)107         public Builder addInformativeResponse(
108                 @NonNull final InformativeResponse informativeResponse) {
109             informativeResponsesBuilder().add(informativeResponse);
110             return this;
111         }
112 
autoBuild()113         abstract ResponseControlData autoBuild();
114         /** Returns the response control data built. */
build()115         public ResponseControlData build() {
116             ResponseControlData responseControlData = autoBuild();
117             Objects.requireNonNull(responseControlData.getInformativeResponses());
118             HttpStatusCodeUtil.checkIsFinalStatusCode(responseControlData.getFinalStatusCode());
119             return responseControlData;
120         }
121     }
122 }
123