Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* vim: ts=4 sw=4 expandtab:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.geckoview;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import org.mozilla.gecko.annotation.WrapForJNI;
/**
* WebResponse represents an HTTP[S] response. It is normally created by {@link
* GeckoWebExecutor#fetch(WebRequest)}.
*/
@WrapForJNI
@AnyThread
public class WebResponse extends WebMessage {
/** The default read timeout for the {@link #body} stream. */
public static final long DEFAULT_READ_TIMEOUT_MS = 30000;
/** The HTTP status code for the response, e.g. 200. */
public final int statusCode;
/** A boolean indicating whether or not this response is the result of a redirection. */
public final boolean redirected;
/** Whether or not this response was delivered via a secure connection. */
public final boolean isSecure;
/** The server certificate used with this response, if any. */
public final @Nullable X509Certificate certificate;
/**
* An {@link InputStream} containing the response body, if available. Attention: the stream must
* be closed whenever the app is done with it, even when the body is ignored. Otherwise the
* connection will not be closed until the stream is garbage collected
*/
public final @Nullable InputStream body;
/**
* Specifies that the contents should request to be opened in another Android application. For
* example, provide PDF content and set this to true to request that Android opens the PDF in a
* system PDF viewer (if possible and allowed by the user).
*/
public final @Nullable boolean requestExternalApp;
/**
* Specifies that the app may skip requesting the download in the UI. A confirmation of the
* download will still be shown.
*/
public final @Nullable boolean skipConfirmation;
protected WebResponse(final @NonNull Builder builder) {
super(builder);
this.statusCode = builder.mStatusCode;
this.redirected = builder.mRedirected;
this.body = builder.mBody;
this.requestExternalApp = builder.mRequestExternalApp;
this.skipConfirmation = builder.mSkipConfirmation;
this.isSecure = builder.mIsSecure;
this.certificate = builder.mCertificate;
this.setReadTimeoutMillis(DEFAULT_READ_TIMEOUT_MS);
}
/**
* Sets the maximum amount of time to wait for data in the {@link #body} read() method. By
* default, the read timeout is set to {@link #DEFAULT_READ_TIMEOUT_MS}.
*
* <p>If 0, there will be no timeout and read() will block indefinitely.
*
* @param millis The duration in milliseconds for the timeout.
*/
public void setReadTimeoutMillis(final long millis) {
if (this.body != null && this.body instanceof GeckoInputStream) {
((GeckoInputStream) this.body).setReadTimeoutMillis(millis);
}
}
/** Builder offers a convenient way to create WebResponse instances. */
@WrapForJNI
@AnyThread
public static class Builder extends WebMessage.Builder {
/* package */ int mStatusCode;
/* package */ boolean mRedirected;
/* package */ InputStream mBody;
/* package */ boolean mRequestExternalApp = false;
/* package */ boolean mSkipConfirmation = false;
/* package */ boolean mIsSecure;
/* package */ X509Certificate mCertificate;
/**
* Constructs a new Builder instance with the specified URI.
*
* @param uri A URI String.
*/
public Builder(final @NonNull String uri) {
super(uri);
}
@Override
public @NonNull Builder uri(final @NonNull String uri) {
super.uri(uri);
return this;
}
@Override
public @NonNull Builder header(final @NonNull String key, final @NonNull String value) {
super.header(key, value);
return this;
}
@Override
public @NonNull Builder addHeader(final @NonNull String key, final @NonNull String value) {
super.addHeader(key, value);
return this;
}
/**
* Sets the {@link InputStream} containing the body of this response.
*
* @param stream An {@link InputStream} with the body of the response.
* @return This Builder instance.
*/
public @NonNull Builder body(final @NonNull InputStream stream) {
mBody = stream;
return this;
}
/**
* Requests that the content be passed to an external Android application. The default is false.
* For example, set to true to request that the user have the option to open the content in
* another Android application.
*
* @param requestExternalApp request that the content be opened in another application.
* @return This Builder instance.
*/
public @NonNull Builder requestExternalApp(final boolean requestExternalApp) {
mRequestExternalApp = requestExternalApp;
return this;
}
/**
* Specifies if a confirmation to begin downloading is necessary or not. (The confirmation that
* a download occurred will still be shown.) The default is false, which is to request a
* download confirmation. Skipping the confirmation is only advisable if the user has already
* opted to download.
*
* @param skipConfirmation whether to skip or show the confirm download flow
* @return This Builder instance.
*/
public @NonNull Builder skipConfirmation(final boolean skipConfirmation) {
mSkipConfirmation = skipConfirmation;
return this;
}
/**
* @param isSecure Whether or not this response is secure.
* @return This Builder instance.
*/
public @NonNull Builder isSecure(final boolean isSecure) {
mIsSecure = isSecure;
return this;
}
/**
* @param certificate The certificate used.
* @return This Builder instance.
*/
public @NonNull Builder certificate(final @NonNull X509Certificate certificate) {
mCertificate = certificate;
return this;
}
/**
* @param encodedCert The certificate used, encoded via DER. Only used via JNI.
*/
@WrapForJNI(exceptionMode = "nsresult")
private void certificateBytes(final @NonNull byte[] encodedCert) {
try {
final CertificateFactory factory = CertificateFactory.getInstance("X.509");
final X509Certificate cert =
(X509Certificate) factory.generateCertificate(new ByteArrayInputStream(encodedCert));
certificate(cert);
} catch (final CertificateException e) {
throw new IllegalArgumentException("Unable to parse DER certificate");
}
}
/**
* Set the HTTP status code, e.g. 200.
*
* @param code A int representing the HTTP status code.
* @return This Builder instance.
*/
public @NonNull Builder statusCode(final int code) {
mStatusCode = code;
return this;
}
/**
* Set whether or not this response was the result of a redirect.
*
* @param redirected A boolean representing whether or not the request was redirected.
* @return This Builder instance.
*/
public @NonNull Builder redirected(final boolean redirected) {
mRedirected = redirected;
return this;
}
/**
* @return A {@link WebResponse} constructed with the values from this Builder instance.
*/
public @NonNull WebResponse build() {
return new WebResponse(this);
}
}
}