428 lines
15 KiB
Java
428 lines
15 KiB
Java
package okhttp3.internal.connection;
|
|
|
|
import java.io.IOException;
|
|
import java.lang.ref.Reference;
|
|
import java.lang.ref.WeakReference;
|
|
import java.net.Socket;
|
|
import java.util.List;
|
|
import okhttp3.Address;
|
|
import okhttp3.Call;
|
|
import okhttp3.ConnectionPool;
|
|
import okhttp3.EventListener;
|
|
import okhttp3.Interceptor;
|
|
import okhttp3.OkHttpClient;
|
|
import okhttp3.Route;
|
|
import okhttp3.internal.Internal;
|
|
import okhttp3.internal.Util;
|
|
import okhttp3.internal.connection.RouteSelector;
|
|
import okhttp3.internal.http.HttpCodec;
|
|
import okhttp3.internal.http2.ConnectionShutdownException;
|
|
import okhttp3.internal.http2.ErrorCode;
|
|
import okhttp3.internal.http2.StreamResetException;
|
|
|
|
/* loaded from: classes2.dex */
|
|
public final class StreamAllocation {
|
|
static final /* synthetic */ boolean $assertionsDisabled = false;
|
|
public final Address address;
|
|
public final Call call;
|
|
private final Object callStackTrace;
|
|
private boolean canceled;
|
|
private HttpCodec codec;
|
|
private RealConnection connection;
|
|
private final ConnectionPool connectionPool;
|
|
public final EventListener eventListener;
|
|
private int refusedStreamCount;
|
|
private boolean released;
|
|
private boolean reportedAcquired;
|
|
private Route route;
|
|
private RouteSelector.Selection routeSelection;
|
|
private final RouteSelector routeSelector;
|
|
|
|
public static final class StreamAllocationReference extends WeakReference<StreamAllocation> {
|
|
public final Object callStackTrace;
|
|
|
|
StreamAllocationReference(StreamAllocation streamAllocation, Object obj) {
|
|
super(streamAllocation);
|
|
this.callStackTrace = obj;
|
|
}
|
|
}
|
|
|
|
public StreamAllocation(ConnectionPool connectionPool, Address address, Call call, EventListener eventListener, Object obj) {
|
|
this.connectionPool = connectionPool;
|
|
this.address = address;
|
|
this.call = call;
|
|
this.eventListener = eventListener;
|
|
this.routeSelector = new RouteSelector(address, routeDatabase(), call, eventListener);
|
|
this.callStackTrace = obj;
|
|
}
|
|
|
|
private Socket deallocate(boolean z, boolean z2, boolean z3) {
|
|
Socket socket;
|
|
if (z3) {
|
|
this.codec = null;
|
|
}
|
|
if (z2) {
|
|
this.released = true;
|
|
}
|
|
RealConnection realConnection = this.connection;
|
|
if (realConnection != null) {
|
|
if (z) {
|
|
realConnection.noNewStreams = true;
|
|
}
|
|
if (this.codec == null && (this.released || this.connection.noNewStreams)) {
|
|
release(this.connection);
|
|
if (this.connection.allocations.isEmpty()) {
|
|
this.connection.idleAtNanos = System.nanoTime();
|
|
if (Internal.instance.connectionBecameIdle(this.connectionPool, this.connection)) {
|
|
socket = this.connection.socket();
|
|
this.connection = null;
|
|
return socket;
|
|
}
|
|
}
|
|
socket = null;
|
|
this.connection = null;
|
|
return socket;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private RealConnection findConnection(int i, int i2, int i3, int i4, boolean z) throws IOException {
|
|
Socket releaseIfNoNewStreams;
|
|
Socket socket;
|
|
RealConnection realConnection;
|
|
RealConnection realConnection2;
|
|
RealConnection realConnection3;
|
|
Route route;
|
|
boolean z2;
|
|
boolean z3;
|
|
RouteSelector.Selection selection;
|
|
synchronized (this.connectionPool) {
|
|
if (this.released) {
|
|
throw new IllegalStateException("released");
|
|
}
|
|
if (this.codec != null) {
|
|
throw new IllegalStateException("codec != null");
|
|
}
|
|
if (this.canceled) {
|
|
throw new IOException("Canceled");
|
|
}
|
|
RealConnection realConnection4 = this.connection;
|
|
releaseIfNoNewStreams = releaseIfNoNewStreams();
|
|
socket = null;
|
|
if (this.connection != null) {
|
|
realConnection2 = this.connection;
|
|
realConnection = null;
|
|
} else {
|
|
realConnection = realConnection4;
|
|
realConnection2 = null;
|
|
}
|
|
if (!this.reportedAcquired) {
|
|
realConnection = null;
|
|
}
|
|
if (realConnection2 == null) {
|
|
Internal.instance.get(this.connectionPool, this.address, this, null);
|
|
if (this.connection != null) {
|
|
realConnection3 = this.connection;
|
|
route = null;
|
|
z2 = true;
|
|
} else {
|
|
route = this.route;
|
|
realConnection3 = realConnection2;
|
|
}
|
|
} else {
|
|
realConnection3 = realConnection2;
|
|
route = null;
|
|
}
|
|
z2 = false;
|
|
}
|
|
Util.closeQuietly(releaseIfNoNewStreams);
|
|
if (realConnection != null) {
|
|
this.eventListener.connectionReleased(this.call, realConnection);
|
|
}
|
|
if (z2) {
|
|
this.eventListener.connectionAcquired(this.call, realConnection3);
|
|
}
|
|
if (realConnection3 != null) {
|
|
return realConnection3;
|
|
}
|
|
if (route != null || ((selection = this.routeSelection) != null && selection.hasNext())) {
|
|
z3 = false;
|
|
} else {
|
|
this.routeSelection = this.routeSelector.next();
|
|
z3 = true;
|
|
}
|
|
synchronized (this.connectionPool) {
|
|
if (this.canceled) {
|
|
throw new IOException("Canceled");
|
|
}
|
|
if (z3) {
|
|
List<Route> all = this.routeSelection.getAll();
|
|
int size = all.size();
|
|
int i5 = 0;
|
|
while (true) {
|
|
if (i5 >= size) {
|
|
break;
|
|
}
|
|
Route route2 = all.get(i5);
|
|
Internal.instance.get(this.connectionPool, this.address, this, route2);
|
|
if (this.connection != null) {
|
|
realConnection3 = this.connection;
|
|
this.route = route2;
|
|
z2 = true;
|
|
break;
|
|
}
|
|
i5++;
|
|
}
|
|
}
|
|
if (!z2) {
|
|
if (route == null) {
|
|
route = this.routeSelection.next();
|
|
}
|
|
this.route = route;
|
|
this.refusedStreamCount = 0;
|
|
realConnection3 = new RealConnection(this.connectionPool, route);
|
|
acquire(realConnection3, false);
|
|
}
|
|
}
|
|
if (z2) {
|
|
this.eventListener.connectionAcquired(this.call, realConnection3);
|
|
return realConnection3;
|
|
}
|
|
realConnection3.connect(i, i2, i3, i4, z, this.call, this.eventListener);
|
|
routeDatabase().connected(realConnection3.route());
|
|
synchronized (this.connectionPool) {
|
|
this.reportedAcquired = true;
|
|
Internal.instance.put(this.connectionPool, realConnection3);
|
|
if (realConnection3.isMultiplexed()) {
|
|
socket = Internal.instance.deduplicate(this.connectionPool, this.address, this);
|
|
realConnection3 = this.connection;
|
|
}
|
|
}
|
|
Util.closeQuietly(socket);
|
|
this.eventListener.connectionAcquired(this.call, realConnection3);
|
|
return realConnection3;
|
|
}
|
|
|
|
private RealConnection findHealthyConnection(int i, int i2, int i3, int i4, boolean z, boolean z2) throws IOException {
|
|
while (true) {
|
|
RealConnection findConnection = findConnection(i, i2, i3, i4, z);
|
|
synchronized (this.connectionPool) {
|
|
if (findConnection.successCount == 0) {
|
|
return findConnection;
|
|
}
|
|
if (findConnection.isHealthy(z2)) {
|
|
return findConnection;
|
|
}
|
|
noNewStreams();
|
|
}
|
|
}
|
|
}
|
|
|
|
private Socket releaseIfNoNewStreams() {
|
|
RealConnection realConnection = this.connection;
|
|
if (realConnection == null || !realConnection.noNewStreams) {
|
|
return null;
|
|
}
|
|
return deallocate(false, false, true);
|
|
}
|
|
|
|
private RouteDatabase routeDatabase() {
|
|
return Internal.instance.routeDatabase(this.connectionPool);
|
|
}
|
|
|
|
public void acquire(RealConnection realConnection, boolean z) {
|
|
if (this.connection != null) {
|
|
throw new IllegalStateException();
|
|
}
|
|
this.connection = realConnection;
|
|
this.reportedAcquired = z;
|
|
realConnection.allocations.add(new StreamAllocationReference(this, this.callStackTrace));
|
|
}
|
|
|
|
public void cancel() {
|
|
HttpCodec httpCodec;
|
|
RealConnection realConnection;
|
|
synchronized (this.connectionPool) {
|
|
this.canceled = true;
|
|
httpCodec = this.codec;
|
|
realConnection = this.connection;
|
|
}
|
|
if (httpCodec != null) {
|
|
httpCodec.cancel();
|
|
} else if (realConnection != null) {
|
|
realConnection.cancel();
|
|
}
|
|
}
|
|
|
|
public HttpCodec codec() {
|
|
HttpCodec httpCodec;
|
|
synchronized (this.connectionPool) {
|
|
httpCodec = this.codec;
|
|
}
|
|
return httpCodec;
|
|
}
|
|
|
|
public synchronized RealConnection connection() {
|
|
return this.connection;
|
|
}
|
|
|
|
public boolean hasMoreRoutes() {
|
|
RouteSelector.Selection selection;
|
|
return this.route != null || ((selection = this.routeSelection) != null && selection.hasNext()) || this.routeSelector.hasNext();
|
|
}
|
|
|
|
public HttpCodec newStream(OkHttpClient okHttpClient, Interceptor.Chain chain, boolean z) {
|
|
try {
|
|
HttpCodec newCodec = findHealthyConnection(chain.connectTimeoutMillis(), chain.readTimeoutMillis(), chain.writeTimeoutMillis(), okHttpClient.pingIntervalMillis(), okHttpClient.retryOnConnectionFailure(), z).newCodec(okHttpClient, chain, this);
|
|
synchronized (this.connectionPool) {
|
|
this.codec = newCodec;
|
|
}
|
|
return newCodec;
|
|
} catch (IOException e) {
|
|
throw new RouteException(e);
|
|
}
|
|
}
|
|
|
|
public void noNewStreams() {
|
|
RealConnection realConnection;
|
|
Socket deallocate;
|
|
synchronized (this.connectionPool) {
|
|
realConnection = this.connection;
|
|
deallocate = deallocate(true, false, false);
|
|
if (this.connection != null) {
|
|
realConnection = null;
|
|
}
|
|
}
|
|
Util.closeQuietly(deallocate);
|
|
if (realConnection != null) {
|
|
this.eventListener.connectionReleased(this.call, realConnection);
|
|
}
|
|
}
|
|
|
|
public void release() {
|
|
RealConnection realConnection;
|
|
Socket deallocate;
|
|
synchronized (this.connectionPool) {
|
|
realConnection = this.connection;
|
|
deallocate = deallocate(false, true, false);
|
|
if (this.connection != null) {
|
|
realConnection = null;
|
|
}
|
|
}
|
|
Util.closeQuietly(deallocate);
|
|
if (realConnection != null) {
|
|
this.eventListener.connectionReleased(this.call, realConnection);
|
|
this.eventListener.callEnd(this.call);
|
|
}
|
|
}
|
|
|
|
public Socket releaseAndAcquire(RealConnection realConnection) {
|
|
if (this.codec != null || this.connection.allocations.size() != 1) {
|
|
throw new IllegalStateException();
|
|
}
|
|
Reference<StreamAllocation> reference = this.connection.allocations.get(0);
|
|
Socket deallocate = deallocate(true, false, false);
|
|
this.connection = realConnection;
|
|
realConnection.allocations.add(reference);
|
|
return deallocate;
|
|
}
|
|
|
|
public Route route() {
|
|
return this.route;
|
|
}
|
|
|
|
public void streamFailed(IOException iOException) {
|
|
boolean z;
|
|
RealConnection realConnection;
|
|
Socket deallocate;
|
|
synchronized (this.connectionPool) {
|
|
if (iOException instanceof StreamResetException) {
|
|
ErrorCode errorCode = ((StreamResetException) iOException).errorCode;
|
|
if (errorCode == ErrorCode.REFUSED_STREAM) {
|
|
this.refusedStreamCount++;
|
|
if (this.refusedStreamCount > 1) {
|
|
this.route = null;
|
|
z = true;
|
|
}
|
|
z = false;
|
|
} else {
|
|
if (errorCode != ErrorCode.CANCEL) {
|
|
this.route = null;
|
|
z = true;
|
|
}
|
|
z = false;
|
|
}
|
|
} else {
|
|
if (this.connection != null && (!this.connection.isMultiplexed() || (iOException instanceof ConnectionShutdownException))) {
|
|
if (this.connection.successCount == 0) {
|
|
if (this.route != null && iOException != null) {
|
|
this.routeSelector.connectFailed(this.route, iOException);
|
|
}
|
|
this.route = null;
|
|
}
|
|
z = true;
|
|
}
|
|
z = false;
|
|
}
|
|
realConnection = this.connection;
|
|
deallocate = deallocate(z, false, true);
|
|
if (this.connection != null || !this.reportedAcquired) {
|
|
realConnection = null;
|
|
}
|
|
}
|
|
Util.closeQuietly(deallocate);
|
|
if (realConnection != null) {
|
|
this.eventListener.connectionReleased(this.call, realConnection);
|
|
}
|
|
}
|
|
|
|
public void streamFinished(boolean z, HttpCodec httpCodec, long j, IOException iOException) {
|
|
RealConnection realConnection;
|
|
Socket deallocate;
|
|
boolean z2;
|
|
this.eventListener.responseBodyEnd(this.call, j);
|
|
synchronized (this.connectionPool) {
|
|
if (httpCodec != null) {
|
|
if (httpCodec == this.codec) {
|
|
if (!z) {
|
|
this.connection.successCount++;
|
|
}
|
|
realConnection = this.connection;
|
|
deallocate = deallocate(z, false, true);
|
|
if (this.connection != null) {
|
|
realConnection = null;
|
|
}
|
|
z2 = this.released;
|
|
}
|
|
}
|
|
throw new IllegalStateException("expected " + this.codec + " but was " + httpCodec);
|
|
}
|
|
Util.closeQuietly(deallocate);
|
|
if (realConnection != null) {
|
|
this.eventListener.connectionReleased(this.call, realConnection);
|
|
}
|
|
if (iOException != null) {
|
|
this.eventListener.callFailed(this.call, iOException);
|
|
} else if (z2) {
|
|
this.eventListener.callEnd(this.call);
|
|
}
|
|
}
|
|
|
|
public String toString() {
|
|
RealConnection connection = connection();
|
|
return connection != null ? connection.toString() : this.address.toString();
|
|
}
|
|
|
|
private void release(RealConnection realConnection) {
|
|
int size = realConnection.allocations.size();
|
|
for (int i = 0; i < size; i++) {
|
|
if (realConnection.allocations.get(i).get() == this) {
|
|
realConnection.allocations.remove(i);
|
|
return;
|
|
}
|
|
}
|
|
throw new IllegalStateException();
|
|
}
|
|
}
|