@Override public Cancellable getSocket(final AsyncHttpClientMiddleware.GetSocketData data) { final URI uri = data.request.getUri(); final int port = getSchemePort(data.request.getUri()); if (port == -1) { return null; } ConnectionInfo info = getConnectionInfo(uri.getScheme(), uri.getHost(), port); if (info.openCount >= maxConnectionCount) { // wait for a connection queue to free up SimpleCancellable queueCancel = new SimpleCancellable(); info.queue.add(data); return queueCancel; } info.openCount++; final String lookup = computeLookup(uri, port, data.request); data.state.putBoolean(getClass().getCanonicalName() + ".owned", true); synchronized (this) { final HashSet<AsyncSocket> sockets = mSockets.get(lookup); if (sockets != null) { for (final AsyncSocket socket : sockets) { if (socket.isOpen()) { sockets.remove(socket); socket.setClosedCallback(null); mClient .getServer() .post( new Runnable() { @Override public void run() { data.request.logd("Reusing keep-alive socket"); data.connectCallback.onConnectCompleted(null, socket); } }); // replace above code with immediate callback? // data.request.logd("Reusing keep-alive socket"); // data.connectCallback.onConnectCompleted(null, socket); // just a noop/dummy, as this can't actually be cancelled. return new SimpleCancellable(); } } } } if (!connectAllAddresses || proxyHost != null || data.request.getProxyHost() != null) { // just default to connecting to a single address data.request.logd("Connecting socket"); String unresolvedHost; int unresolvedPort; if (data.request.getProxyHost() != null) { unresolvedHost = data.request.getProxyHost(); unresolvedPort = data.request.getProxyPort(); // set the host and port explicitly for proxied connections data.request .getHeaders() .getHeaders() .setStatusLine(data.request.getProxyRequestLine().toString()); } else if (proxyHost != null) { unresolvedHost = proxyHost; unresolvedPort = proxyPort; // set the host and port explicitly for proxied connections data.request .getHeaders() .getHeaders() .setStatusLine(data.request.getProxyRequestLine().toString()); } else { unresolvedHost = uri.getHost(); unresolvedPort = port; } return mClient .getServer() .connectSocket( unresolvedHost, unresolvedPort, wrapCallback(data.connectCallback, uri, port)); } // try to connect to everything... data.request.logv("Resolving domain and connecting to all available addresses"); return mClient .getServer() .getAllByName(uri.getHost()) .then( new TransformFuture<AsyncSocket, InetAddress[]>() { Exception lastException; @Override protected void error(Exception e) { super.error(e); data.connectCallback.onConnectCompleted(e, null); } @Override protected void transform(final InetAddress[] result) throws Exception { Continuation keepTrying = new Continuation( new CompletedCallback() { @Override public void onCompleted(Exception ex) { // if it completed, that means that the connection failed if (lastException == null) lastException = new ConnectionFailedException( "Unable to connect to remote address"); setComplete(lastException); } }); for (final InetAddress address : result) { keepTrying.add( new ContinuationCallback() { @Override public void onContinue( Continuation continuation, final CompletedCallback next) throws Exception { mClient .getServer() .connectSocket( new InetSocketAddress(address, port), wrapCallback( new ConnectCallback() { @Override public void onConnectCompleted( Exception ex, AsyncSocket socket) { if (isDone()) { lastException = new Exception("internal error during connect"); next.onCompleted(null); return; } // try the next address if (ex != null) { lastException = ex; next.onCompleted(null); return; } // if the socket is no longer needed, just hang onto it... if (isDone() || isCancelled()) { data.request.logd( "Recycling extra socket leftover from cancelled operation"); idleSocket(socket); recycleSocket(socket, data.request); return; } if (setComplete(null, socket)) { data.connectCallback.onConnectCompleted(ex, socket); } } }, uri, port)); } }); } keepTrying.start(); } }); }
// step 1) see if we can serve request from the cache directly. // also see if this can be turned into a conditional cache request. @Override public Cancellable getSocket(final GetSocketData data) { if (cache == null) return null; if (!caching) return null; if (data.request.getHeaders().isNoCache()) return null; // Log.i(LOGTAG, "getting cache socket: " + request.getUri().toString()); String key = uriToKey(data.request.getUri()); DiskLruCache.Snapshot snapshot; Entry entry; try { snapshot = cache.get(key); if (snapshot == null) { // Log.i(LOGTAG, "snapshot fail"); return null; } entry = new Entry(snapshot.getInputStream(ENTRY_METADATA)); } catch (IOException e) { // Give up because the cache cannot be read. return null; } if (!entry.matches( data.request.getUri(), data.request.getMethod(), data.request.getHeaders().getHeaders().toMultimap())) { snapshot.close(); return null; } ResponseSource responseSource = ResponseSource.NETWORK; CacheResponse candidate = entry.isHttps() ? new EntrySecureCacheResponse(entry, snapshot) : new EntryCacheResponse(entry, snapshot); Map<String, List<String>> responseHeadersMap; InputStream cachedResponseBody; try { responseHeadersMap = candidate.getHeaders(); cachedResponseBody = candidate.getBody(); } catch (Exception e) { return null; } if (responseHeadersMap == null || cachedResponseBody == null) { try { cachedResponseBody.close(); } catch (Exception e) { } return null; } RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(responseHeadersMap); ResponseHeaders cachedResponseHeaders = new ResponseHeaders(data.request.getUri(), rawResponseHeaders); long now = System.currentTimeMillis(); responseSource = cachedResponseHeaders.chooseResponseSource(now, data.request.getHeaders()); if (responseSource == ResponseSource.CACHE) { cacheStoreCount++; final CachedSocket socket = entry.isHttps() ? new CachedSSLSocket((EntrySecureCacheResponse) candidate) : new CachedSocket((EntryCacheResponse) candidate); client .getServer() .post( new Runnable() { @Override public void run() { data.connectCallback.onConnectCompleted(null, socket); socket.spewInternal(); } }); } else if (responseSource == ResponseSource.CONDITIONAL_CACHE) { CacheData cacheData = new CacheData(); cacheData.cachedResponseHeaders = cachedResponseHeaders; cacheData.candidate = candidate; data.state.putParcelable("cache-data", cacheData); return null; } else { // NETWORK or other try { cachedResponseBody.close(); } catch (Exception e) { } return null; } return new SimpleCancelable(); }