1
16 package okhttp3;
17
18 import java.io.IOException;
19 import java.io.InterruptedIOException;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.concurrent.ExecutorService;
23 import java.util.concurrent.RejectedExecutionException;
24 import java.util.concurrent.atomic.AtomicInteger;
25 import okhttp3.internal.NamedRunnable;
26 import okhttp3.internal.cache.CacheInterceptor;
27 import okhttp3.internal.connection.ConnectInterceptor;
28 import okhttp3.internal.connection.Transmitter;
29 import okhttp3.internal.http.BridgeInterceptor;
30 import okhttp3.internal.http.CallServerInterceptor;
31 import okhttp3.internal.http.RealInterceptorChain;
32 import okhttp3.internal.http.RetryAndFollowUpInterceptor;
33 import okhttp3.internal.platform.Platform;
34 import okio.Timeout;
35
36 import static okhttp3.internal.Util.closeQuietly;
37 import static okhttp3.internal.platform.Platform.INFO;
38
39 final class RealCall implements Call {
40 final OkHttpClient client;
41
42
46 private Transmitter transmitter;
47
48
49 final Request originalRequest;
50 final boolean forWebSocket;
51
52
53 private boolean executed;
54
55 private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
56 this.client = client;
57 this.originalRequest = originalRequest;
58 this.forWebSocket = forWebSocket;
59 }
60
61 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
62
63 RealCall call = new RealCall(client, originalRequest, forWebSocket);
64 call.transmitter = new Transmitter(client, call);
65 return call;
66 }
67
68 @Override public Request request() {
69 return originalRequest;
70 }
71
72 @Override public Response execute() throws IOException {
73 synchronized (this) {
74 if (executed) throw new IllegalStateException("Already Executed");
75 executed = true;
76 }
77 transmitter.timeoutEnter();
78 transmitter.callStart();
79 try {
80 client.dispatcher().executed(this);
81 return getResponseWithInterceptorChain();
82 } finally {
83 client.dispatcher().finished(this);
84 }
85 }
86
87 @Override public void enqueue(Callback responseCallback) {
88 synchronized (this) {
89 if (executed) throw new IllegalStateException("Already Executed");
90 executed = true;
91 }
92 transmitter.callStart();
93 client.dispatcher().enqueue(new AsyncCall(responseCallback));
94 }
95
96 @Override public void cancel() {
97 transmitter.cancel();
98 }
99
100 @Override public Timeout timeout() {
101 return transmitter.timeout();
102 }
103
104 @Override public synchronized boolean isExecuted() {
105 return executed;
106 }
107
108 @Override public boolean isCanceled() {
109 return transmitter.isCanceled();
110 }
111
112 @SuppressWarnings("CloneDoesntCallSuperClone")
113 @Override public RealCall clone() {
114 return RealCall.newRealCall(client, originalRequest, forWebSocket);
115 }
116
117 final class AsyncCall extends NamedRunnable {
118 private final Callback responseCallback;
119 private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
120
121 AsyncCall(Callback responseCallback) {
122 super("OkHttp %s", redactedUrl());
123 this.responseCallback = responseCallback;
124 }
125
126 AtomicInteger callsPerHost() {
127 return callsPerHost;
128 }
129
130 void reuseCallsPerHostFrom(AsyncCall other) {
131 this.callsPerHost = other.callsPerHost;
132 }
133
134 String host() {
135 return originalRequest.url().host();
136 }
137
138 Request request() {
139 return originalRequest;
140 }
141
142 RealCall get() {
143 return RealCall.this;
144 }
145
146
150 void executeOn(ExecutorService executorService) {
151 assert (!Thread.holdsLock(client.dispatcher()));
152 boolean success = false;
153 try {
154 executorService.execute(this);
155 success = true;
156 } catch (RejectedExecutionException e) {
157 InterruptedIOException ioException = new InterruptedIOException("executor rejected");
158 ioException.initCause(e);
159 transmitter.noMoreExchanges(ioException);
160 responseCallback.onFailure(RealCall.this, ioException);
161 } finally {
162 if (!success) {
163 client.dispatcher().finished(this);
164 }
165 }
166 }
167
168 @Override protected void execute() {
169 boolean signalledCallback = false;
170 transmitter.timeoutEnter();
171 try {
172 Response response = getResponseWithInterceptorChain();
173 signalledCallback = true;
174 responseCallback.onResponse(RealCall.this, response);
175 } catch (IOException e) {
176 if (signalledCallback) {
177
178 Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
179 } else {
180 responseCallback.onFailure(RealCall.this, e);
181 }
182 } catch (Throwable t) {
183 cancel();
184 if (!signalledCallback) {
185 IOException canceledException = new IOException("canceled due to " + t);
186 canceledException.addSuppressed(t);
187 responseCallback.onFailure(RealCall.this, canceledException);
188 }
189 throw t;
190 } finally {
191 client.dispatcher().finished(this);
192 }
193 }
194 }
195
196
200 String toLoggableString() {
201 return (isCanceled() ? "canceled " : "")
202 + (forWebSocket ? "web socket" : "call")
203 + " to " + redactedUrl();
204 }
205
206 String redactedUrl() {
207 return originalRequest.url().redact();
208 }
209
210 Response getResponseWithInterceptorChain() throws IOException {
211
212 List<Interceptor> interceptors = new ArrayList<>();
213 interceptors.addAll(client.interceptors());
214 interceptors.add(new RetryAndFollowUpInterceptor(client));
215 interceptors.add(new BridgeInterceptor(client.cookieJar()));
216 interceptors.add(new CacheInterceptor(client.internalCache()));
217 interceptors.add(new ConnectInterceptor(client));
218 if (!forWebSocket) {
219 interceptors.addAll(client.networkInterceptors());
220 }
221 interceptors.add(new CallServerInterceptor(forWebSocket));
222
223 Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
224 originalRequest, this, client.connectTimeoutMillis(),
225 client.readTimeoutMillis(), client.writeTimeoutMillis());
226
227 boolean calledNoMoreExchanges = false;
228 try {
229 Response response = chain.proceed(originalRequest);
230 if (transmitter.isCanceled()) {
231 closeQuietly(response);
232 throw new IOException("Canceled");
233 }
234 return response;
235 } catch (IOException e) {
236 calledNoMoreExchanges = true;
237 throw transmitter.noMoreExchanges(e);
238 } finally {
239 if (!calledNoMoreExchanges) {
240 transmitter.noMoreExchanges(null);
241 }
242 }
243 }
244 }
245