xref: /aosp_15_r20/external/cronet/components/cronet/android/fake/java/org/chromium/net/test/FakeUrlResponse.java (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.net.test;
6 
7 import org.chromium.net.UrlResponseInfo;
8 
9 import java.io.UnsupportedEncodingException;
10 import java.util.AbstractMap;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Objects;
17 
18 // TODO(kirchman): Update this to explain inter-class usage once other classes land.
19 /**
20  *
21  * Fake response model for UrlRequest used by Fake Cronet.
22  */
23 public class FakeUrlResponse {
24     private final int mHttpStatusCode;
25     // Entries to mAllHeadersList should never be mutated.
26     private final List<Map.Entry<String, String>> mAllHeadersList;
27     private final boolean mWasCached;
28     private final String mNegotiatedProtocol;
29     private final String mProxyServer;
30     private final byte[] mResponseBody;
31 
getNullableOrDefault(T nullableObject, T defaultObject)32     private static <T extends Object> T getNullableOrDefault(T nullableObject, T defaultObject) {
33         if (nullableObject != null) {
34             return nullableObject;
35         }
36         return defaultObject;
37     }
38 
39     /**
40      * Constructs a {@link FakeUrlResponse} from a {@link FakeUrlResponse.Builder}.
41      * @param builder the {@link FakeUrlResponse.Builder} to create the response from
42      */
FakeUrlResponse(Builder builder)43     private FakeUrlResponse(Builder builder) {
44         mHttpStatusCode = builder.mHttpStatusCode;
45         mAllHeadersList = Collections.unmodifiableList(new ArrayList<>(builder.mAllHeadersList));
46         mWasCached = builder.mWasCached;
47         mNegotiatedProtocol = builder.mNegotiatedProtocol;
48         mProxyServer = builder.mProxyServer;
49         mResponseBody = builder.mResponseBody;
50     }
51 
52     /**
53      * Constructs a {@link FakeUrlResponse} from a {@link UrlResponseInfo}. All nullable fields in
54      * the {@link UrlResponseInfo} are initialized to the default value if the provided value is
55      * null.
56      *
57      * @param info the {@link UrlResponseInfo} used to initialize this object's fields
58      */
FakeUrlResponse(UrlResponseInfo info)59     public FakeUrlResponse(UrlResponseInfo info) {
60         mHttpStatusCode = info.getHttpStatusCode();
61         mAllHeadersList = Collections.unmodifiableList(new ArrayList<>(info.getAllHeadersAsList()));
62         mWasCached = info.wasCached();
63         mNegotiatedProtocol =
64                 getNullableOrDefault(
65                         info.getNegotiatedProtocol(), Builder.DEFAULT_NEGOTIATED_PROTOCOL);
66         mProxyServer = getNullableOrDefault(info.getProxyServer(), Builder.DEFAULT_PROXY_SERVER);
67         mResponseBody = Builder.DEFAULT_RESPONSE_BODY;
68     }
69 
70     /** Builds a {@link FakeUrlResponse}. */
71     public static class Builder {
72         private static final int DEFAULT_HTTP_STATUS_CODE = 200;
73         private static final List<Map.Entry<String, String>> INTERNAL_INITIAL_HEADERS_LIST =
74                 new ArrayList<>();
75         private static final boolean DEFAULT_WAS_CACHED = false;
76         private static final String DEFAULT_NEGOTIATED_PROTOCOL = "";
77         private static final String DEFAULT_PROXY_SERVER = "";
78         private static final byte[] DEFAULT_RESPONSE_BODY = new byte[0];
79 
80         private int mHttpStatusCode = DEFAULT_HTTP_STATUS_CODE;
81         // Entries to mAllHeadersList should never be mutated.
82         private List<Map.Entry<String, String>> mAllHeadersList =
83                 new ArrayList<>(INTERNAL_INITIAL_HEADERS_LIST);
84         private boolean mWasCached = DEFAULT_WAS_CACHED;
85         private String mNegotiatedProtocol = DEFAULT_NEGOTIATED_PROTOCOL;
86         private String mProxyServer = DEFAULT_PROXY_SERVER;
87         private byte[] mResponseBody = DEFAULT_RESPONSE_BODY;
88 
89         /** Constructs a {@link FakeUrlResponse.Builder} with the default parameters. */
Builder()90         public Builder() {}
91 
92         /**
93          * Constructs a {@link FakeUrlResponse.Builder} from a source {@link FakeUrlResponse}.
94          *
95          * @param source a {@link FakeUrlResponse} to copy into this {@link FakeUrlResponse.Builder}
96          */
Builder(FakeUrlResponse source)97         private Builder(FakeUrlResponse source) {
98             mHttpStatusCode = source.getHttpStatusCode();
99             mAllHeadersList = new ArrayList<>(source.getAllHeadersList());
100             mWasCached = source.getWasCached();
101             mNegotiatedProtocol = source.getNegotiatedProtocol();
102             mProxyServer = source.getProxyServer();
103             mResponseBody = source.getResponseBody();
104         }
105 
106         /**
107          * Sets the HTTP status code. The default value is 200.
108          *
109          * @param httpStatusCode for {@link UrlResponseInfo.getHttpStatusCode()}
110          * @return the builder with the corresponding HTTP status code set
111          */
setHttpStatusCode(int httpStatusCode)112         public Builder setHttpStatusCode(int httpStatusCode) {
113             mHttpStatusCode = httpStatusCode;
114             return this;
115         }
116 
117         /**
118          * Adds a response header to built {@link FakeUrlResponse}s.
119          *
120          * @param name  the name of the header key, for example, "location" for a redirect header
121          * @param value the header value
122          * @return the builder with the corresponding header set
123          */
addHeader(String name, String value)124         public Builder addHeader(String name, String value) {
125             mAllHeadersList.add(new AbstractMap.SimpleEntry<>(name, value));
126             return this;
127         }
128 
129         /**
130          * Sets result of {@link UrlResponseInfo.wasCached()}. The default wasCached value is false.
131          *
132          * @param wasCached for {@link UrlResponseInfo.wasCached()}
133          * @return the builder with the corresponding wasCached field set
134          */
setWasCached(boolean wasCached)135         public Builder setWasCached(boolean wasCached) {
136             mWasCached = wasCached;
137             return this;
138         }
139 
140         /**
141          * Sets result of {@link UrlResponseInfo.getNegotiatedProtocol()}. The default negotiated
142          * protocol is an empty string.
143          *
144          * @param negotiatedProtocol for {@link UrlResponseInfo.getNegotiatedProtocol()}
145          * @return the builder with the corresponding negotiatedProtocol field set
146          */
setNegotiatedProtocol(String negotiatedProtocol)147         public Builder setNegotiatedProtocol(String negotiatedProtocol) {
148             mNegotiatedProtocol = negotiatedProtocol;
149             return this;
150         }
151 
152         /**
153          * Sets result of {@link UrlResponseInfo.getProxyServer()}. The default proxy server is an
154          * empty string.
155          *
156          * @param proxyServer for {@link UrlResponseInfo.getProxyServer()}
157          * @return the builder with the corresponding proxyServer field set
158          */
setProxyServer(String proxyServer)159         public Builder setProxyServer(String proxyServer) {
160             mProxyServer = proxyServer;
161             return this;
162         }
163 
164         /**
165          * Sets the response body for a response. The default response body is an empty byte array.
166          *
167          * @param body all the information the server returns
168          * @return the builder with the corresponding responseBody field set
169          */
setResponseBody(byte[] body)170         public Builder setResponseBody(byte[] body) {
171             mResponseBody = body;
172             return this;
173         }
174 
175         /**
176          * Constructs a {@link FakeUrlResponse} from this {@link FakeUrlResponse.Builder}.
177          *
178          * @return a FakeUrlResponse with all fields set according to this builder
179          */
build()180         public FakeUrlResponse build() {
181             return new FakeUrlResponse(this);
182         }
183     }
184 
185     /**
186      * Returns the HTTP status code.
187      *
188      * @return the HTTP status code.
189      */
getHttpStatusCode()190     int getHttpStatusCode() {
191         return mHttpStatusCode;
192     }
193 
194     /**
195      * Returns an unmodifiable list of the response header key and value pairs.
196      *
197      * @return an unmodifiable list of response header key and value pairs
198      */
getAllHeadersList()199     List<Map.Entry<String, String>> getAllHeadersList() {
200         return mAllHeadersList;
201     }
202 
203     /**
204      * Returns the wasCached value for this response.
205      *
206      * @return the wasCached value for this response
207      */
getWasCached()208     boolean getWasCached() {
209         return mWasCached;
210     }
211 
212     /**
213      * Returns the protocol (for example 'quic/1+spdy/3') negotiated with the server.
214      *
215      * @return the protocol negotiated with the server
216      */
getNegotiatedProtocol()217     String getNegotiatedProtocol() {
218         return mNegotiatedProtocol;
219     }
220 
221     /**
222      * Returns the proxy server that was used for the request.
223      *
224      * @return the proxy server that was used for the request
225      */
getProxyServer()226     String getProxyServer() {
227         return mProxyServer;
228     }
229 
230     /**
231      * Returns the body of the response as a byte array. Used for {@link UrlRequest.Callback}
232      * {@code read()} callback.
233      *
234      * @return the response body
235      */
getResponseBody()236     byte[] getResponseBody() {
237         return mResponseBody;
238     }
239 
240     /**
241      * Returns a mutable builder representation of this {@link FakeUrlResponse}
242      *
243      * @return a {@link FakeUrlResponse.Builder} with all fields copied from this instance.
244      */
toBuilder()245     public Builder toBuilder() {
246         return new Builder(this);
247     }
248 
249     @Override
equals(Object otherObj)250     public boolean equals(Object otherObj) {
251         if (!(otherObj instanceof FakeUrlResponse)) {
252             return false;
253         }
254         FakeUrlResponse other = (FakeUrlResponse) otherObj;
255         return (mHttpStatusCode == other.mHttpStatusCode
256                 && mAllHeadersList.equals(other.mAllHeadersList)
257                 && mWasCached == other.mWasCached
258                 && mNegotiatedProtocol.equals(other.mNegotiatedProtocol)
259                 && mProxyServer.equals(other.mProxyServer)
260                 && Arrays.equals(mResponseBody, other.mResponseBody));
261     }
262 
263     @Override
hashCode()264     public int hashCode() {
265         return Objects.hash(
266                 mHttpStatusCode,
267                 mAllHeadersList,
268                 mWasCached,
269                 mNegotiatedProtocol,
270                 mProxyServer,
271                 Arrays.hashCode(mResponseBody));
272     }
273 
274     @Override
toString()275     public String toString() {
276         StringBuilder outputString = new StringBuilder();
277         outputString.append("HTTP Status Code: " + mHttpStatusCode);
278         outputString.append(" Headers: " + mAllHeadersList.toString());
279         outputString.append(" Was Cached: " + mWasCached);
280         outputString.append(" Negotiated Protocol: " + mNegotiatedProtocol);
281         outputString.append(" Proxy Server: " + mProxyServer);
282         outputString.append(" Response Body ");
283         try {
284             String bodyString = new String(mResponseBody, "UTF-8");
285             outputString.append("(UTF-8): " + bodyString);
286         } catch (UnsupportedEncodingException e) {
287             outputString.append("(hexadecimal): " + getHexStringFromBytes(mResponseBody));
288         }
289         return outputString.toString();
290     }
291 
getHexStringFromBytes(byte[] bytes)292     private String getHexStringFromBytes(byte[] bytes) {
293         StringBuilder bytesToHexStringBuilder = new StringBuilder();
294         for (byte b : mResponseBody) {
295             bytesToHexStringBuilder.append(String.format("%02x", b));
296         }
297         return bytesToHexStringBuilder.toString();
298     }
299 }
300