1
16 package okhttp3.internal.connection;
17
18 import java.io.IOException;
19 import java.net.Socket;
20 import java.util.List;
21 import okhttp3.Address;
22 import okhttp3.Call;
23 import okhttp3.EventListener;
24 import okhttp3.Interceptor;
25 import okhttp3.OkHttpClient;
26 import okhttp3.Route;
27 import okhttp3.internal.Util;
28 import okhttp3.internal.http.ExchangeCodec;
29
30 import static okhttp3.internal.Util.closeQuietly;
31
32
53 final class ExchangeFinder {
54 private final Transmitter transmitter;
55 private final Address address;
56 private final RealConnectionPool connectionPool;
57 private final Call call;
58 private final EventListener eventListener;
59
60 private RouteSelector.Selection routeSelection;
61
62
63 private final RouteSelector routeSelector;
64 private RealConnection connectingConnection;
65 private boolean hasStreamFailure;
66 private Route nextRouteToTry;
67
68 ExchangeFinder(Transmitter transmitter, RealConnectionPool connectionPool,
69 Address address, Call call, EventListener eventListener) {
70 this.transmitter = transmitter;
71 this.connectionPool = connectionPool;
72 this.address = address;
73 this.call = call;
74 this.eventListener = eventListener;
75 this.routeSelector = new RouteSelector(
76 address, connectionPool.routeDatabase, call, eventListener);
77 }
78
79 public ExchangeCodec find(
80 OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
81 int connectTimeout = chain.connectTimeoutMillis();
82 int readTimeout = chain.readTimeoutMillis();
83 int writeTimeout = chain.writeTimeoutMillis();
84 int pingIntervalMillis = client.pingIntervalMillis();
85 boolean connectionRetryEnabled = client.retryOnConnectionFailure();
86
87 try {
88 RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
89 writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
90 return resultConnection.newCodec(client, chain);
91 } catch (RouteException e) {
92 trackFailure();
93 throw e;
94 } catch (IOException e) {
95 trackFailure();
96 throw new RouteException(e);
97 }
98 }
99
100
104 private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
105 int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
106 boolean doExtensiveHealthChecks) throws IOException {
107 while (true) {
108 RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
109 pingIntervalMillis, connectionRetryEnabled);
110
111
112 synchronized (connectionPool) {
113 if (candidate.successCount == 0 && !candidate.isMultiplexed()) {
114 return candidate;
115 }
116 }
117
118
119
120 if (!candidate.isHealthy(doExtensiveHealthChecks)) {
121 candidate.noNewExchanges();
122 continue;
123 }
124
125 return candidate;
126 }
127 }
128
129
133 private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
134 int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
135 boolean foundPooledConnection = false;
136 RealConnection result = null;
137 Route selectedRoute = null;
138 RealConnection releasedConnection;
139 Socket toClose;
140 synchronized (connectionPool) {
141 if (transmitter.isCanceled()) throw new IOException("Canceled");
142 hasStreamFailure = false;
143
144
145
146 releasedConnection = transmitter.connection;
147 toClose = transmitter.connection != null && transmitter.connection.noNewExchanges
148 ? transmitter.releaseConnectionNoEvents()
149 : null;
150
151 if (transmitter.connection != null) {
152
153 result = transmitter.connection;
154 releasedConnection = null;
155 }
156
157 if (result == null) {
158
159 if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {
160 foundPooledConnection = true;
161 result = transmitter.connection;
162 } else if (nextRouteToTry != null) {
163 selectedRoute = nextRouteToTry;
164 nextRouteToTry = null;
165 } else if (retryCurrentRoute()) {
166 selectedRoute = transmitter.connection.route();
167 }
168 }
169 }
170 closeQuietly(toClose);
171
172 if (releasedConnection != null) {
173 eventListener.connectionReleased(call, releasedConnection);
174 }
175 if (foundPooledConnection) {
176 eventListener.connectionAcquired(call, result);
177 }
178 if (result != null) {
179
180 return result;
181 }
182
183
184 boolean newRouteSelection = false;
185 if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
186 newRouteSelection = true;
187 routeSelection = routeSelector.next();
188 }
189
190 List<Route> routes = null;
191 synchronized (connectionPool) {
192 if (transmitter.isCanceled()) throw new IOException("Canceled");
193
194 if (newRouteSelection) {
195
196
197 routes = routeSelection.getAll();
198 if (connectionPool.transmitterAcquirePooledConnection(
199 address, transmitter, routes, false)) {
200 foundPooledConnection = true;
201 result = transmitter.connection;
202 }
203 }
204
205 if (!foundPooledConnection) {
206 if (selectedRoute == null) {
207 selectedRoute = routeSelection.next();
208 }
209
210
211
212 result = new RealConnection(connectionPool, selectedRoute);
213 connectingConnection = result;
214 }
215 }
216
217
218 if (foundPooledConnection) {
219 eventListener.connectionAcquired(call, result);
220 return result;
221 }
222
223
224 result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
225 connectionRetryEnabled, call, eventListener);
226 connectionPool.routeDatabase.connected(result.route());
227
228 Socket socket = null;
229 synchronized (connectionPool) {
230 connectingConnection = null;
231
232
233 if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, true)) {
234
235 result.noNewExchanges = true;
236 socket = result.socket();
237 result = transmitter.connection;
238
239
240
241 nextRouteToTry = selectedRoute;
242 } else {
243 connectionPool.put(result);
244 transmitter.acquireConnectionNoEvents(result);
245 }
246 }
247 closeQuietly(socket);
248
249 eventListener.connectionAcquired(call, result);
250 return result;
251 }
252
253 RealConnection connectingConnection() {
254 assert (Thread.holdsLock(connectionPool));
255 return connectingConnection;
256 }
257
258 void trackFailure() {
259 assert (!Thread.holdsLock(connectionPool));
260 synchronized (connectionPool) {
261 hasStreamFailure = true;
262 }
263 }
264
265
266 boolean hasStreamFailure() {
267 synchronized (connectionPool) {
268 return hasStreamFailure;
269 }
270 }
271
272
273 boolean hasRouteToTry() {
274 synchronized (connectionPool) {
275 if (nextRouteToTry != null) {
276 return true;
277 }
278 if (retryCurrentRoute()) {
279
280 nextRouteToTry = transmitter.connection.route();
281 return true;
282 }
283 return (routeSelection != null && routeSelection.hasNext())
284 || routeSelector.hasNext();
285 }
286 }
287
288
293 private boolean retryCurrentRoute() {
294 return transmitter.connection != null
295 && transmitter.connection.routeFailureCount == 0
296 && Util.sameConnection(transmitter.connection.route().address().url(), address.url());
297 }
298 }
299