| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.net.test; |
| |
| import org.chromium.net.UrlResponseInfo; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.util.AbstractMap; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| |
| // TODO(kirchman): Update this to explain inter-class usage once other classes land. |
| /** |
| * |
| * Fake response model for UrlRequest used by Fake Cronet. |
| */ |
| public class FakeUrlResponse { |
| private final int mHttpStatusCode; |
| // Entries to mAllHeadersList should never be mutated. |
| private final List<Map.Entry<String, String>> mAllHeadersList; |
| private final boolean mWasCached; |
| private final String mNegotiatedProtocol; |
| private final String mProxyServer; |
| private final byte[] mResponseBody; |
| |
| private static <T extends Object> T getNullableOrDefault(T nullableObject, T defaultObject) { |
| if (nullableObject != null) { |
| return nullableObject; |
| } |
| return defaultObject; |
| } |
| |
| /** |
| * Constructs a {@link FakeUrlResponse} from a {@link FakeUrlResponse.Builder}. |
| * @param builder the {@link FakeUrlResponse.Builder} to create the response from |
| */ |
| private FakeUrlResponse(Builder builder) { |
| mHttpStatusCode = builder.mHttpStatusCode; |
| mAllHeadersList = Collections.unmodifiableList(new ArrayList<>(builder.mAllHeadersList)); |
| mWasCached = builder.mWasCached; |
| mNegotiatedProtocol = builder.mNegotiatedProtocol; |
| mProxyServer = builder.mProxyServer; |
| mResponseBody = builder.mResponseBody; |
| } |
| |
| /** |
| * Constructs a {@link FakeUrlResponse} from a {@link UrlResponseInfo}. All nullable fields in |
| * the {@link UrlResponseInfo} are initialized to the default value if the provided value is |
| * null. |
| * |
| * @param info the {@link UrlResponseInfo} used to initialize this object's fields |
| */ |
| public FakeUrlResponse(UrlResponseInfo info) { |
| mHttpStatusCode = info.getHttpStatusCode(); |
| mAllHeadersList = Collections.unmodifiableList(new ArrayList<>(info.getAllHeadersAsList())); |
| mWasCached = info.wasCached(); |
| mNegotiatedProtocol = |
| getNullableOrDefault( |
| info.getNegotiatedProtocol(), Builder.DEFAULT_NEGOTIATED_PROTOCOL); |
| mProxyServer = getNullableOrDefault(info.getProxyServer(), Builder.DEFAULT_PROXY_SERVER); |
| mResponseBody = Builder.DEFAULT_RESPONSE_BODY; |
| } |
| |
| /** Builds a {@link FakeUrlResponse}. */ |
| public static class Builder { |
| private static final int DEFAULT_HTTP_STATUS_CODE = 200; |
| private static final List<Map.Entry<String, String>> INTERNAL_INITIAL_HEADERS_LIST = |
| new ArrayList<>(); |
| private static final boolean DEFAULT_WAS_CACHED = false; |
| private static final String DEFAULT_NEGOTIATED_PROTOCOL = ""; |
| private static final String DEFAULT_PROXY_SERVER = ""; |
| private static final byte[] DEFAULT_RESPONSE_BODY = new byte[0]; |
| |
| private int mHttpStatusCode = DEFAULT_HTTP_STATUS_CODE; |
| // Entries to mAllHeadersList should never be mutated. |
| private List<Map.Entry<String, String>> mAllHeadersList = |
| new ArrayList<>(INTERNAL_INITIAL_HEADERS_LIST); |
| private boolean mWasCached = DEFAULT_WAS_CACHED; |
| private String mNegotiatedProtocol = DEFAULT_NEGOTIATED_PROTOCOL; |
| private String mProxyServer = DEFAULT_PROXY_SERVER; |
| private byte[] mResponseBody = DEFAULT_RESPONSE_BODY; |
| |
| /** Constructs a {@link FakeUrlResponse.Builder} with the default parameters. */ |
| public Builder() {} |
| |
| /** |
| * Constructs a {@link FakeUrlResponse.Builder} from a source {@link FakeUrlResponse}. |
| * |
| * @param source a {@link FakeUrlResponse} to copy into this {@link FakeUrlResponse.Builder} |
| */ |
| private Builder(FakeUrlResponse source) { |
| mHttpStatusCode = source.getHttpStatusCode(); |
| mAllHeadersList = new ArrayList<>(source.getAllHeadersList()); |
| mWasCached = source.getWasCached(); |
| mNegotiatedProtocol = source.getNegotiatedProtocol(); |
| mProxyServer = source.getProxyServer(); |
| mResponseBody = source.getResponseBody(); |
| } |
| |
| /** |
| * Sets the HTTP status code. The default value is 200. |
| * |
| * @param httpStatusCode for {@link UrlResponseInfo.getHttpStatusCode()} |
| * @return the builder with the corresponding HTTP status code set |
| */ |
| public Builder setHttpStatusCode(int httpStatusCode) { |
| mHttpStatusCode = httpStatusCode; |
| return this; |
| } |
| |
| /** |
| * Adds a response header to built {@link FakeUrlResponse}s. |
| * |
| * @param name the name of the header key, for example, "location" for a redirect header |
| * @param value the header value |
| * @return the builder with the corresponding header set |
| */ |
| public Builder addHeader(String name, String value) { |
| mAllHeadersList.add(new AbstractMap.SimpleEntry<>(name, value)); |
| return this; |
| } |
| |
| /** |
| * Sets result of {@link UrlResponseInfo.wasCached()}. The default wasCached value is false. |
| * |
| * @param wasCached for {@link UrlResponseInfo.wasCached()} |
| * @return the builder with the corresponding wasCached field set |
| */ |
| public Builder setWasCached(boolean wasCached) { |
| mWasCached = wasCached; |
| return this; |
| } |
| |
| /** |
| * Sets result of {@link UrlResponseInfo.getNegotiatedProtocol()}. The default negotiated |
| * protocol is an empty string. |
| * |
| * @param negotiatedProtocol for {@link UrlResponseInfo.getNegotiatedProtocol()} |
| * @return the builder with the corresponding negotiatedProtocol field set |
| */ |
| public Builder setNegotiatedProtocol(String negotiatedProtocol) { |
| mNegotiatedProtocol = negotiatedProtocol; |
| return this; |
| } |
| |
| /** |
| * Sets result of {@link UrlResponseInfo.getProxyServer()}. The default proxy server is an |
| * empty string. |
| * |
| * @param proxyServer for {@link UrlResponseInfo.getProxyServer()} |
| * @return the builder with the corresponding proxyServer field set |
| */ |
| public Builder setProxyServer(String proxyServer) { |
| mProxyServer = proxyServer; |
| return this; |
| } |
| |
| /** |
| * Sets the response body for a response. The default response body is an empty byte array. |
| * |
| * @param body all the information the server returns |
| * @return the builder with the corresponding responseBody field set |
| */ |
| public Builder setResponseBody(byte[] body) { |
| mResponseBody = body; |
| return this; |
| } |
| |
| /** |
| * Constructs a {@link FakeUrlResponse} from this {@link FakeUrlResponse.Builder}. |
| * |
| * @return a FakeUrlResponse with all fields set according to this builder |
| */ |
| public FakeUrlResponse build() { |
| return new FakeUrlResponse(this); |
| } |
| } |
| |
| /** |
| * Returns the HTTP status code. |
| * |
| * @return the HTTP status code. |
| */ |
| int getHttpStatusCode() { |
| return mHttpStatusCode; |
| } |
| |
| /** |
| * Returns an unmodifiable list of the response header key and value pairs. |
| * |
| * @return an unmodifiable list of response header key and value pairs |
| */ |
| List<Map.Entry<String, String>> getAllHeadersList() { |
| return mAllHeadersList; |
| } |
| |
| /** |
| * Returns the wasCached value for this response. |
| * |
| * @return the wasCached value for this response |
| */ |
| boolean getWasCached() { |
| return mWasCached; |
| } |
| |
| /** |
| * Returns the protocol (for example 'quic/1+spdy/3') negotiated with the server. |
| * |
| * @return the protocol negotiated with the server |
| */ |
| String getNegotiatedProtocol() { |
| return mNegotiatedProtocol; |
| } |
| |
| /** |
| * Returns the proxy server that was used for the request. |
| * |
| * @return the proxy server that was used for the request |
| */ |
| String getProxyServer() { |
| return mProxyServer; |
| } |
| |
| /** |
| * Returns the body of the response as a byte array. Used for {@link UrlRequest.Callback} |
| * {@code read()} callback. |
| * |
| * @return the response body |
| */ |
| byte[] getResponseBody() { |
| return mResponseBody; |
| } |
| |
| /** |
| * Returns a mutable builder representation of this {@link FakeUrlResponse} |
| * |
| * @return a {@link FakeUrlResponse.Builder} with all fields copied from this instance. |
| */ |
| public Builder toBuilder() { |
| return new Builder(this); |
| } |
| |
| @Override |
| public boolean equals(Object otherObj) { |
| if (!(otherObj instanceof FakeUrlResponse)) { |
| return false; |
| } |
| FakeUrlResponse other = (FakeUrlResponse) otherObj; |
| return (mHttpStatusCode == other.mHttpStatusCode |
| && mAllHeadersList.equals(other.mAllHeadersList) |
| && mWasCached == other.mWasCached |
| && mNegotiatedProtocol.equals(other.mNegotiatedProtocol) |
| && mProxyServer.equals(other.mProxyServer) |
| && Arrays.equals(mResponseBody, other.mResponseBody)); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash( |
| mHttpStatusCode, |
| mAllHeadersList, |
| mWasCached, |
| mNegotiatedProtocol, |
| mProxyServer, |
| Arrays.hashCode(mResponseBody)); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder outputString = new StringBuilder(); |
| outputString.append("HTTP Status Code: " + mHttpStatusCode); |
| outputString.append(" Headers: " + mAllHeadersList.toString()); |
| outputString.append(" Was Cached: " + mWasCached); |
| outputString.append(" Negotiated Protocol: " + mNegotiatedProtocol); |
| outputString.append(" Proxy Server: " + mProxyServer); |
| outputString.append(" Response Body "); |
| try { |
| String bodyString = new String(mResponseBody, "UTF-8"); |
| outputString.append("(UTF-8): " + bodyString); |
| } catch (UnsupportedEncodingException e) { |
| outputString.append("(hexadecimal): " + getHexStringFromBytes(mResponseBody)); |
| } |
| return outputString.toString(); |
| } |
| |
| private String getHexStringFromBytes(byte[] bytes) { |
| StringBuilder bytesToHexStringBuilder = new StringBuilder(); |
| for (byte b : mResponseBody) { |
| bytesToHexStringBuilder.append(String.format("%02x", b)); |
| } |
| return bytesToHexStringBuilder.toString(); |
| } |
| } |