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.android.internal.util.Preconditions; 24 25 import com.google.auto.value.AutoValue; 26 27 import java.util.Arrays; 28 import java.util.Objects; 29 30 /** 31 * Binary Representation of HTTP Messages. 32 * 33 * @see <a href="https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html">Binary 34 * HTTP</a> 35 */ 36 @AutoValue 37 public abstract class BinaryHttpMessage extends BinaryHttpSerializableComponent { 38 private static final int FRAMING_INDICATOR_LENGTH_IN_BYTES = 1; 39 static final byte[] EMPTY_CONTENT = new byte[0]; 40 private static final int FRAMING_INDICATOR_SECTION_COUNT = 1; 41 private static final int CONTENT_SECTION_COUNT = 2; 42 43 /** Returns the framing indicator of the message. */ 44 @FramingIndicator getFramingIndicator()45 public abstract byte getFramingIndicator(); 46 47 @NonNull getControlData()48 abstract ControlData getControlData(); 49 50 /** Returns the header fields of the message. */ 51 @NonNull getHeaderFields()52 public abstract Fields getHeaderFields(); 53 54 /** Returns the content of the message. */ 55 @NonNull 56 @SuppressWarnings("mutable") getContent()57 public abstract byte[] getContent(); 58 59 /** 60 * By definition, content and trailer can be omitted without zero length indicator. The length 61 * of padding does not have a strict definition, we define the length as following: 62 * 63 * <ol> 64 * <li>As we do not support trailer, we treat it as padding part for now. 65 * <li>If content has zero length, we calculate padding after the length field. 66 * <li>If length is omitted in the bytes, we return 0 for padding length. 67 * <ol/> 68 * 69 * @return the length of padding. 70 */ getPaddingLength()71 public abstract int getPaddingLength(); 72 73 /** Returns if the message represents a request. */ isRequest()74 public boolean isRequest() { 75 return FramingIndicator.FRAMING_INDICATOR_REQUEST_OF_KNOWN_LENGTH == getFramingIndicator(); 76 } 77 78 /** Returns if the message represents a response. */ isResponse()79 public boolean isResponse() { 80 return FramingIndicator.FRAMING_INDICATOR_RESPONSE_OF_KNOWN_LENGTH == getFramingIndicator(); 81 } 82 83 /** Returns request control data if the message is a request message. */ 84 @NonNull getRequestControlData()85 public RequestControlData getRequestControlData() { 86 if (isRequest()) { 87 return (RequestControlData) getControlData(); 88 } 89 throw new IllegalArgumentException("Message does not represent a Request."); 90 } 91 92 /** Returns response control data if the message is a response message. */ 93 @NonNull getResponseControlData()94 public ResponseControlData getResponseControlData() { 95 if (isResponse()) { 96 return (ResponseControlData) getControlData(); 97 } 98 throw new IllegalArgumentException("Message does not represent a Response."); 99 } 100 101 /** Serialize the message to a binary representation according to the framing indicator. */ 102 @NonNull serialize()103 public byte[] serialize() { 104 switch (getFramingIndicator()) { 105 case FramingIndicator.FRAMING_INDICATOR_REQUEST_OF_KNOWN_LENGTH: 106 case FramingIndicator.FRAMING_INDICATOR_RESPONSE_OF_KNOWN_LENGTH: 107 return combineWithPadding(knownLengthSerialize(), getPaddingLength()); 108 default: 109 throw new IllegalArgumentException("Invalid framing indicator."); 110 } 111 } 112 113 /** Get a builder for known length request. */ 114 @NonNull knownLengthRequestBuilder( @onNull final RequestControlData requestControlData)115 public static Builder knownLengthRequestBuilder( 116 @NonNull final RequestControlData requestControlData) { 117 return builder() 118 .setFramingIndicator(FramingIndicator.FRAMING_INDICATOR_REQUEST_OF_KNOWN_LENGTH) 119 .setControlData(requestControlData); 120 } 121 122 /** Get a builder for known length response. */ 123 @NonNull knownLengthResponseBuilder( @onNull final ResponseControlData responseControlData)124 public static Builder knownLengthResponseBuilder( 125 @NonNull final ResponseControlData responseControlData) { 126 return builder() 127 .setFramingIndicator(FramingIndicator.FRAMING_INDICATOR_RESPONSE_OF_KNOWN_LENGTH) 128 .setControlData(responseControlData); 129 } 130 131 @NonNull builder()132 static Builder builder() { 133 return new AutoValue_BinaryHttpMessage.Builder() 134 .setContent(EMPTY_CONTENT) 135 .setPaddingLength(0); 136 } 137 138 @Override getKnownLengthSerializedSectionsCount()139 int getKnownLengthSerializedSectionsCount() { 140 return FRAMING_INDICATOR_SECTION_COUNT 141 + getControlData().getKnownLengthSerializedSectionsCount() 142 + getHeaderFields().getKnownLengthSerializedSectionsCount() 143 + CONTENT_SECTION_COUNT; 144 } 145 146 /** 147 * {@inheritDoc} 148 * 149 * @return [framing indicator][control data sections]*n[header fields sections]*n[content 150 * length][content] 151 * @see RequestControlData#knownLengthSerialize() 152 * @see ResponseControlData#knownLengthSerialize() 153 * @see Fields#knownLengthSerialize() 154 */ 155 @Override knownLengthSerialize()156 byte[][] knownLengthSerialize() { 157 byte[] framingIndicator = new byte[] {getFramingIndicator()}; 158 // For response, the informative responses are included in the control data to simplifying 159 // the code. 160 byte[][] controlData = getControlData().knownLengthSerialize(); 161 byte[][] headerFields = getHeaderFields().knownLengthSerialize(); 162 byte[][] content = new byte[][] {toFrc9000Int(getContent().length), getContent()}; 163 // We don't have trailer support for now. 164 165 int totalSections = 166 framingIndicator.length + controlData.length + headerFields.length + content.length; 167 byte[][] result = new byte[totalSections][]; 168 result[0] = framingIndicator; 169 int position = FRAMING_INDICATOR_LENGTH_IN_BYTES; 170 System.arraycopy(controlData, 0, result, position, controlData.length); 171 position += controlData.length; 172 System.arraycopy(headerFields, 0, result, position, headerFields.length); 173 position += headerFields.length; 174 System.arraycopy(content, 0, result, position, content.length); 175 return result; 176 } 177 178 @NonNull combineWithPadding(@onNull final byte[][] sections, final int paddingLength)179 private byte[] combineWithPadding(@NonNull final byte[][] sections, final int paddingLength) { 180 int pointer = 0; 181 int totalSize = Arrays.stream(sections).mapToInt(s -> s.length).sum() + paddingLength; 182 byte[] result = new byte[totalSize]; 183 for (byte[] section : sections) { 184 for (byte b : section) { 185 result[pointer++] = b; 186 } 187 } 188 return result; 189 } 190 191 /** Padding length is ignored as it does not have any info. */ 192 @Override equals(Object o)193 public final boolean equals(Object o) { 194 if (this == o) return true; 195 if (!(o instanceof BinaryHttpMessage)) return false; 196 BinaryHttpMessage that = (BinaryHttpMessage) o; 197 return getFramingIndicator() == that.getFramingIndicator() 198 && getControlData().equals(that.getControlData()) 199 && getHeaderFields().equals(that.getHeaderFields()) 200 && Arrays.equals(getContent(), that.getContent()); 201 } 202 203 @Override hashCode()204 public final int hashCode() { 205 return Objects.hash( 206 getFramingIndicator(), 207 getControlData(), 208 getHeaderFields(), 209 Arrays.hashCode(getContent())); 210 } 211 212 /** Builder for {@link BinaryHttpMessage}. */ 213 @AutoValue.Builder 214 public abstract static class Builder { 215 /** Set framing indicator for the message. */ 216 @NonNull setFramingIndicator(byte framingIndicator)217 abstract Builder setFramingIndicator(byte framingIndicator); 218 /** Set control data for the message. */ 219 @NonNull setControlData(@onNull ControlData controlData)220 abstract Builder setControlData(@NonNull ControlData controlData); 221 222 /** Set header fields for the message. */ 223 @NonNull setHeaderFields(@onNull Fields headerFields)224 public abstract Builder setHeaderFields(@NonNull Fields headerFields); 225 226 /** Sets message content. */ 227 @NonNull setContent(@onNull byte[] content)228 public abstract Builder setContent(@NonNull byte[] content); 229 230 /** Sets padding length of the message. */ 231 @NonNull setPaddingLength(int paddingLength)232 public abstract Builder setPaddingLength(int paddingLength); 233 getPaddingLength()234 abstract int getPaddingLength(); 235 236 @NonNull autoBuild()237 abstract BinaryHttpMessage autoBuild(); 238 239 /** Returns the message built. */ 240 @NonNull build()241 public BinaryHttpMessage build() { 242 BinaryHttpMessage binaryHttpMessage = autoBuild(); 243 Preconditions.checkArgumentNonnegative(getPaddingLength()); 244 return binaryHttpMessage; 245 } 246 } 247 } 248