/*
* Copyright (C) 2016 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.io.
IOException;
import java.util.
List;
import java.util.concurrent.
TimeUnit;
import okhttp3.
Call;
import okhttp3.
Connection;
import okhttp3.
EventListener;
import okhttp3.
Interceptor;
import okhttp3.
Request;
import okhttp3.
Response;
import okhttp3.internal.connection.
RealConnection;
import okhttp3.internal.connection.
StreamAllocation;
import static okhttp3.internal.
Util.checkDuration;
/**
* A concrete interceptor chain that carries the entire interceptor chain: all application
* interceptors, the OkHttp core, all network interceptors, and finally the network caller.
*/
public final class
RealInterceptorChain implements
Interceptor.
Chain {
private final
List<
Interceptor>
interceptors;
private final
StreamAllocation streamAllocation;
private final
HttpCodec httpCodec;
private final
RealConnection connection;
private final int
index;
private final
Request request;
private final
Call call;
private final
EventListener eventListener;
private final int
connectTimeout;
private final int
readTimeout;
private final int
writeTimeout;
private int
calls;
public
RealInterceptorChain(
List<
Interceptor>
interceptors,
StreamAllocation streamAllocation,
HttpCodec httpCodec,
RealConnection connection, int
index,
Request request,
Call call,
EventListener eventListener, int
connectTimeout, int
readTimeout, int
writeTimeout) {
this.
interceptors =
interceptors;
this.
connection =
connection;
this.
streamAllocation =
streamAllocation;
this.
httpCodec =
httpCodec;
this.
index =
index;
this.
request =
request;
this.
call =
call;
this.
eventListener =
eventListener;
this.
connectTimeout =
connectTimeout;
this.
readTimeout =
readTimeout;
this.
writeTimeout =
writeTimeout;
}
@
Override public
Connection connection() {
return
connection;
}
@
Override public int
connectTimeoutMillis() {
return
connectTimeout;
}
@
Override public
Interceptor.
Chain withConnectTimeout(int
timeout,
TimeUnit unit) {
int
millis =
checkDuration("timeout",
timeout,
unit);
return new
RealInterceptorChain(
interceptors,
streamAllocation,
httpCodec,
connection,
index,
request,
call,
eventListener,
millis,
readTimeout,
writeTimeout);
}
@
Override public int
readTimeoutMillis() {
return
readTimeout;
}
@
Override public
Interceptor.
Chain withReadTimeout(int
timeout,
TimeUnit unit) {
int
millis =
checkDuration("timeout",
timeout,
unit);
return new
RealInterceptorChain(
interceptors,
streamAllocation,
httpCodec,
connection,
index,
request,
call,
eventListener,
connectTimeout,
millis,
writeTimeout);
}
@
Override public int
writeTimeoutMillis() {
return
writeTimeout;
}
@
Override public
Interceptor.
Chain withWriteTimeout(int
timeout,
TimeUnit unit) {
int
millis =
checkDuration("timeout",
timeout,
unit);
return new
RealInterceptorChain(
interceptors,
streamAllocation,
httpCodec,
connection,
index,
request,
call,
eventListener,
connectTimeout,
readTimeout,
millis);
}
public
StreamAllocation streamAllocation() {
return
streamAllocation;
}
public
HttpCodec httpStream() {
return
httpCodec;
}
@
Override public
Call call() {
return
call;
}
public
EventListener eventListener() {
return
eventListener;
}
@
Override public
Request request() {
return
request;
}
@
Override public
Response proceed(
Request request) throws
IOException {
return
proceed(
request,
streamAllocation,
httpCodec,
connection);
}
public
Response proceed(
Request request,
StreamAllocation streamAllocation,
HttpCodec httpCodec,
RealConnection connection) throws
IOException {
if (
index >=
interceptors.
size()) throw new
AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.
httpCodec != null && !this.
connection.
supportsUrl(
request.
url())) {
throw new
IllegalStateException("network interceptor " +
interceptors.
get(
index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.
httpCodec != null &&
calls > 1) {
throw new
IllegalStateException("network interceptor " +
interceptors.
get(
index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new
RealInterceptorChain(
interceptors,
streamAllocation,
httpCodec,
connection,
index + 1,
request,
call,
eventListener,
connectTimeout,
readTimeout,
writeTimeout);
Interceptor interceptor =
interceptors.
get(
index);
Response response =
interceptor.
intercept(
next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (
httpCodec != null &&
index + 1 <
interceptors.
size() &&
next.
calls != 1) {
throw new
IllegalStateException("network interceptor " +
interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (
response == null) {
throw new
NullPointerException("interceptor " +
interceptor + " returned null");
}
if (
response.
body() == null) {
throw new
IllegalStateException(
"interceptor " +
interceptor + " returned a response with no body");
}
return
response;
}
}