/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.stetho.urlconnection;
import com.facebook.stetho.inspector.network.DefaultResponseHandler;
import com.facebook.stetho.inspector.network.NetworkEventReporter;
import com.facebook.stetho.inspector.network.NetworkEventReporterImpl;
import com.facebook.stetho.inspector.network.RequestBodyHelper;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
/**
* Isolated implementation class to allow us to escape the verifier if Stetho is not
* present. This is done for convenience so that {@link StethoURLConnectionManager} hooks can be
* left in a release build without any significant overhead either at runtime or in the compiled
* APK.
*/
class StethoURLConnectionManagerImpl {
private final NetworkEventReporter mStethoHook = NetworkEventReporterImpl.get();
private final String mRequestId;
@Nullable
private final String mFriendlyName;
private HttpURLConnection mConnection;
@Nullable private URLConnectionInspectorRequest mInspectorRequest;
@Nullable private RequestBodyHelper mRequestBodyHelper;
public StethoURLConnectionManagerImpl(@Nullable String friendlyName) {
mRequestId = mStethoHook.nextRequestId();
mFriendlyName = friendlyName;
}
public boolean isStethoActive() {
return mStethoHook.isEnabled();
}
/**
* @see StethoURLConnectionManager#preConnect
*/
public void preConnect(
HttpURLConnection connection,
@Nullable SimpleRequestEntity requestEntity) {
throwIfConnection();
mConnection = connection;
if (isStethoActive()) {
mRequestBodyHelper = new RequestBodyHelper(mStethoHook, getStethoRequestId());
mInspectorRequest = new URLConnectionInspectorRequest(
getStethoRequestId(),
mFriendlyName,
connection,
requestEntity,
mRequestBodyHelper);
mStethoHook.requestWillBeSent(mInspectorRequest);
}
}
/**
* @see StethoURLConnectionManager#postConnect
*/
public void postConnect() throws IOException {
throwIfNoConnection();
if (isStethoActive()) {
if (mRequestBodyHelper != null && mRequestBodyHelper.hasBody()) {
mRequestBodyHelper.reportDataSent();
}
mStethoHook.responseHeadersReceived(
new URLConnectionInspectorResponse(
getStethoRequestId(),
mConnection));
}
}
/**
* @see StethoURLConnectionManager#httpExchangeFailed
*/
public void httpExchangeFailed(IOException ex) {
throwIfNoConnection();
if (isStethoActive()) {
mStethoHook.httpExchangeFailed(getStethoRequestId(), ex.toString());
}
}
/**
* @see StethoURLConnectionManager#interpretResponseStream
*/
public InputStream interpretResponseStream(@Nullable InputStream responseStream) {
throwIfNoConnection();
if (isStethoActive()) {
// Note that Content-Encoding is stripped out by HttpURLConnection on modern versions of
// Android (fun fact, it's powered by okhttp) when decompression is handled transparently.
// When this occurs, we will not be able to report the compressed size properly. Callers,
// however, can disable this behaviour which will once again give us access to the raw
// Content-Encoding so that we can handle it properly.
responseStream = mStethoHook.interpretResponseStream(
getStethoRequestId(),
mConnection.getHeaderField("Content-Type"),
mConnection.getHeaderField("Content-Encoding"),
responseStream,
new DefaultResponseHandler(mStethoHook, getStethoRequestId()));
}
return responseStream;
}
private void throwIfNoConnection() {
if (mConnection == null) {
throw new IllegalStateException("Must call preConnect");
}
}
private void throwIfConnection() {
if (mConnection != null) {
throw new IllegalStateException("Must not call preConnect twice");
}
}
/**
* Convenience method to access the lower level {@link NetworkEventReporter} API.
*/
public NetworkEventReporter getStethoHook() {
return mStethoHook;
}
/**
* @see StethoURLConnectionManager#getStethoRequestId()
*/
@Nonnull
public String getStethoRequestId() {
return mRequestId;
}
}