@Override public void connect( final HttpClientConnection managedConn, final HttpRoute route, final int connectTimeout, final HttpContext context) throws IOException { Args.notNull(managedConn, "Managed Connection"); Args.notNull(route, "HTTP route"); final ManagedHttpClientConnection conn; synchronized (managedConn) { final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); conn = entry.getConnection(); } final HttpHost host; if (route.getProxyHost() != null) { host = route.getProxyHost(); } else { host = route.getTargetHost(); } final InetSocketAddress localAddress = route.getLocalSocketAddress(); SocketConfig socketConfig = this.configData.getSocketConfig(host); if (socketConfig == null) { socketConfig = this.configData.getDefaultSocketConfig(); } if (socketConfig == null) { socketConfig = SocketConfig.DEFAULT; } this.connectionOperator.connect( conn, host, localAddress, connectTimeout, socketConfig, context); }
public SocketAddress resolveRemoteAddress(final HttpRoute route) { HttpHost firsthop = route.getProxyHost(); if (firsthop == null) { firsthop = route.getTargetHost(); } String hostname = firsthop.getHostName(); int port = firsthop.getPort(); if (port < 0) { Scheme scheme = this.schemeRegistry.getScheme(firsthop); port = scheme.resolvePort(port); } return new InetSocketAddress(hostname, port); }
@Override public ConnectionRequest requestConnection(final HttpRoute route, final Object state) { // If this is the redirect route, stub the return value // so-as to pretend the host is waiting on a slot... if (route.getTargetHost().getHostName().equals("localhost")) { final Thread currentThread = Thread.currentThread(); return new ConnectionRequest() { @Override public boolean cancel() { currentThread.interrupt(); return true; } @Override public HttpClientConnection get(final long timeout, final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { connLatch.countDown(); // notify waiter that we're getting a connection // zero usually means sleep forever, but CountDownLatch doesn't interpret it that way. if (!awaitLatch.await(timeout > 0 ? timeout : Integer.MAX_VALUE, tunit)) { throw new ConnectionPoolTimeoutException(); } return Mockito.mock(HttpClientConnection.class); } }; } else { return super.requestConnection(route, state); } }
/** * Creates the CONNECT request for tunnelling. Called by {@link #createTunnelToTarget * createTunnelToTarget}. * * @param route the route to establish * @param context the context for request execution * @return the CONNECT request for tunnelling */ protected HttpRequest createConnectRequest(HttpRoute route, HttpContext context) { // see RFC 2817, section 5.2 and // INTERNET-DRAFT: Tunneling TCP based protocols through // Web proxy servers HttpHost target = route.getTargetHost(); String host = target.getHostName(); int port = target.getPort(); if (port < 0) { Scheme scheme = connManager.getSchemeRegistry().getScheme(target.getSchemeName()); port = scheme.getDefaultPort(); } StringBuilder buffer = new StringBuilder(host.length() + 6); buffer.append(host); buffer.append(':'); buffer.append(Integer.toString(port)); String authority = buffer.toString(); ProtocolVersion ver = HttpProtocolParams.getVersion(params); HttpRequest req = new BasicHttpRequest("CONNECT", authority, ver); return req; }
@Override public ManagedHttpClientConnection create(final HttpRoute route) throws IOException { ConnectionConfig config = null; if (route.getProxyHost() != null) { config = this.configData.getConnectionConfig(route.getProxyHost()); } if (config == null) { config = this.configData.getConnectionConfig(route.getTargetHost()); } if (config == null) { config = this.configData.getDefaultConnectionConfig(); } if (config == null) { config = ConnectionConfig.DEFAULT; } return this.connFactory.create(route, config); }
@Override protected void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) { if (args != null && args.length >= 2 && args[1] != null && args[1] instanceof HttpRoute) { final HttpRoute route = (HttpRoute) args[1]; final StringBuilder sb = new StringBuilder(); if (route.getProxyHost() != null) { sb.append(route.getProxyHost().getHostName()).append(":"); sb.append(route.getProxyHost().getPort()); } else { sb.append(route.getTargetHost().getHostName()).append(":"); sb.append(route.getTargetHost().getPort()); } recorder.recordAttribute(AnnotationKey.HTTP_INTERNAL_DISPLAY, sb.toString()); } recorder.recordApi(methodDescriptor); recorder.recordServiceType(HttpClient4Constants.HTTP_CLIENT_4_INTERNAL); }
/* 35: */ /* 36: */ public static HttpRoute getForcedRoute(HttpParams params) /* 37: */ { /* 38:119 */ if (params == null) { /* 39:120 */ throw new IllegalArgumentException("Parameters must not be null."); /* 40: */ } /* 41:122 */ HttpRoute route = (HttpRoute) params.getParameter("http.route.forced-route"); /* 42:124 */ if ((route != null) && (NO_ROUTE.equals(route))) { /* 43:126 */ route = null; /* 44: */ } /* 45:128 */ return route; /* 46: */ }
@Override public void upgrade( final HttpClientConnection managedConn, final HttpRoute route, final HttpContext context) throws IOException { Args.notNull(managedConn, "Managed Connection"); Args.notNull(route, "HTTP route"); final ManagedHttpClientConnection conn; synchronized (managedConn) { final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); conn = entry.getConnection(); } this.connectionOperator.upgrade(conn, route.getTargetHost(), context); }
protected void rewriteRequestURI(final RequestWrapper request, final HttpRoute route) throws ProtocolException { try { URI uri = request.getURI(); if (route.getProxyHost() != null && !route.isTunnelled()) { // Make sure the request URI is absolute if (!uri.isAbsolute()) { HttpHost target = route.getTargetHost(); uri = URIUtils.rewriteURI(uri, target); request.setURI(uri); } } else { // Make sure the request URI is relative if (uri.isAbsolute()) { uri = URIUtils.rewriteURI(uri, null); request.setURI(uri); } } } catch (URISyntaxException ex) { throw new ProtocolException("Invalid URI: " + request.getRequestLine().getUri(), ex); } }
public SocketAddress resolveLocalAddress(final HttpRoute route) { return new InetSocketAddress(route.getLocalAddress(), 0); }
/** * Send the request message asynchronously to the given EPR * * @param epr the destination EPR for the message * @param msgContext the message being sent * @throws AxisFault on error */ private void sendAsyncRequest(EndpointReference epr, MessageContext msgContext) throws AxisFault { try { URL url = new URL(epr.getAddress()); String scheme = url.getProtocol() != null ? url.getProtocol() : "http"; String hostname = url.getHost(); int port = url.getPort(); if (port == -1) { // use default if ("http".equals(scheme)) { port = 80; } else if ("https".equals(scheme)) { port = 443; } } HttpHost target = new HttpHost(hostname, port, scheme); boolean secure = "https".equalsIgnoreCase(target.getSchemeName()); HttpHost proxy = proxyConfig.selectProxy(target); HttpRoute route; if (proxy != null) { route = new HttpRoute(target, null, proxy, secure); } else { route = new HttpRoute(target, null, secure); } Axis2HttpRequest axis2Req = new Axis2HttpRequest(epr, route, msgContext); Object timeout = msgContext.getProperty(NhttpConstants.SEND_TIMEOUT); if (timeout != null && timeout instanceof Long) { axis2Req.setTimeout((int) ((Long) timeout).longValue()); } NHttpClientConnection conn = connpool.getConnection(route); // Ensure MessageContext has a ClientConnectionDebug attached before we start streaming ServerConnectionDebug scd = (ServerConnectionDebug) msgContext.getProperty(ServerHandler.SERVER_CONNECTION_DEBUG); ClientConnectionDebug ccd; if (scd != null) { ccd = scd.getClientConnectionDebug(); if (ccd == null) { ccd = new ClientConnectionDebug(scd); scd.setClientConnectionDebug(ccd); } ccd.recordRequestStartTime(conn, axis2Req); msgContext.setProperty(ClientHandler.CLIENT_CONNECTION_DEBUG, ccd); } if (conn == null) { HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost(); ioReactor.connect( new InetSocketAddress(host.getHostName(), host.getPort()), null, axis2Req, sessionRequestCallback); if (log.isDebugEnabled()) { log.debug("A new connection established to : " + route); } } else { conn.setSocketTimeout(socketTimeout); // reinitialize timeouts for the pooled connection try { handler.submitRequest(conn, axis2Req); if (log.isDebugEnabled()) { log.debug("An existing connection reused to : " + hostname + ":" + port); } } catch (ConnectionClosedException e) { ioReactor.connect( new InetSocketAddress(hostname, port), null, axis2Req, sessionRequestCallback); if (log.isDebugEnabled()) { log.debug("A new connection established to : " + hostname + ":" + port); } } } try { axis2Req.streamMessageContents(); } catch (AxisFault af) { throw af; } } catch (MalformedURLException e) { handleException("Malformed destination EPR : " + epr.getAddress(), e); } }
@Override public Header authenticate( final Credentials credentials, final HttpRequest request, final HttpContext context) throws AuthenticationException { Args.notNull(request, "HTTP request"); switch (state) { case UNINITIATED: throw new AuthenticationException( getSchemeName() + " authentication has not been initiated"); case FAILED: throw new AuthenticationException(getSchemeName() + " authentication has failed"); case CHALLENGE_RECEIVED: try { final HttpRoute route = (HttpRoute) context.getAttribute(HttpClientContext.HTTP_ROUTE); if (route == null) { throw new AuthenticationException("Connection route is not available"); } HttpHost host; if (isProxy()) { host = route.getProxyHost(); if (host == null) { host = route.getTargetHost(); } } else { host = route.getTargetHost(); } final String authServer; String hostname = host.getHostName(); if (this.useCanonicalHostname) { try { // TODO: uncomment this statement and delete the resolveCanonicalHostname, // TODO: as soon canonical hostname resolving is implemented in the // SystemDefaultDnsResolver // final DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE; // hostname = dnsResolver.resolveCanonicalHostname(host.getHostName()); hostname = resolveCanonicalHostname(hostname); } catch (UnknownHostException ignore) { } } if (this.stripPort) { // || host.getPort()==80 || host.getPort()==443) { authServer = hostname; } else { authServer = hostname + ":" + host.getPort(); } if (log.isDebugEnabled()) { log.debug("init " + authServer); } token = generateToken(token, authServer, credentials); state = State.TOKEN_GENERATED; } catch (final GSSException gsse) { state = State.FAILED; if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) { throw new InvalidCredentialsException(gsse.getMessage(), gsse); } if (gsse.getMajor() == GSSException.NO_CRED) { throw new InvalidCredentialsException(gsse.getMessage(), gsse); } if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN || gsse.getMajor() == GSSException.DUPLICATE_TOKEN || gsse.getMajor() == GSSException.OLD_TOKEN) { throw new AuthenticationException(gsse.getMessage(), gsse); } // other error throw new AuthenticationException(gsse.getMessage()); } case TOKEN_GENERATED: final String tokenstr = new String(base64codec.encode(token)); if (log.isDebugEnabled()) { log.debug("Sending response '" + tokenstr + "' back to the auth server"); } final CharArrayBuffer buffer = new CharArrayBuffer(32); if (isProxy()) { buffer.append(AUTH.PROXY_AUTH_RESP); } else { buffer.append(AUTH.WWW_AUTH_RESP); } buffer.append(": Negotiate "); buffer.append(tokenstr); return new BufferedHeader(buffer); default: throw new IllegalStateException("Illegal state: " + state); } }
@Override public Header authenticate( final Credentials credentials, final HttpRequest request, final HttpContext context) throws AuthenticationException { Args.notNull(request, "HTTP request"); switch (state) { case UNINITIATED: throw new AuthenticationException( getSchemeName() + " authentication has not been initiated"); case FAILED: throw new AuthenticationException(getSchemeName() + " authentication has failed"); case CHALLENGE_RECEIVED: try { final HttpRoute route = (HttpRoute) context.getAttribute(HttpClientContext.HTTP_ROUTE); if (route == null) { throw new AuthenticationException("Connection route is not available"); } HttpHost host; if (isProxy()) { host = route.getProxyHost(); if (host == null) { host = route.getTargetHost(); } } else { host = route.getTargetHost(); } final String authServer; if (!this.stripPort && host.getPort() > 0) { authServer = host.toHostString(); } else { authServer = host.getHostName(); } if (log.isDebugEnabled()) { log.debug("init " + authServer); } token = generateToken(token, authServer); state = State.TOKEN_GENERATED; } catch (final GSSException gsse) { state = State.FAILED; if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) { throw new InvalidCredentialsException(gsse.getMessage(), gsse); } if (gsse.getMajor() == GSSException.NO_CRED) { throw new InvalidCredentialsException(gsse.getMessage(), gsse); } if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN || gsse.getMajor() == GSSException.DUPLICATE_TOKEN || gsse.getMajor() == GSSException.OLD_TOKEN) { throw new AuthenticationException(gsse.getMessage(), gsse); } // other error throw new AuthenticationException(gsse.getMessage()); } case TOKEN_GENERATED: final String tokenstr = new String(base64codec.encode(token)); if (log.isDebugEnabled()) { log.debug("Sending response '" + tokenstr + "' back to the auth server"); } final CharArrayBuffer buffer = new CharArrayBuffer(32); if (isProxy()) { buffer.append(AUTH.PROXY_AUTH_RESP); } else { buffer.append(AUTH.WWW_AUTH_RESP); } buffer.append(": Negotiate "); buffer.append(tokenstr); return new BufferedHeader(buffer); default: throw new IllegalStateException("Illegal state: " + state); } }
/** * Analyzes a response to check need for a followup. * * @param roureq the request and route. * @param response the response to analayze * @param context the context used for the current request execution * @return the followup request and route if there is a followup, or <code>null</code> if the * response should be returned as is * @throws HttpException in case of a problem * @throws IOException in case of an IO problem */ protected RoutedRequest handleResponse( RoutedRequest roureq, HttpResponse response, HttpContext context) throws HttpException, IOException { HttpRoute route = roureq.getRoute(); HttpHost proxy = route.getProxyHost(); RequestWrapper request = roureq.getRequest(); HttpParams params = request.getParams(); if (HttpClientParams.isRedirecting(params) && this.redirectHandler.isRedirectRequested(response, context)) { if (redirectCount >= maxRedirects) { throw new RedirectException("Maximum redirects (" + maxRedirects + ") exceeded"); } redirectCount++; URI uri = this.redirectHandler.getLocationURI(response, context); HttpHost newTarget = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); HttpGet redirect = new HttpGet(uri); HttpRequest orig = request.getOriginal(); redirect.setHeaders(orig.getAllHeaders()); RequestWrapper wrapper = new RequestWrapper(redirect); wrapper.setParams(params); HttpRoute newRoute = determineRoute(newTarget, wrapper, context); RoutedRequest newRequest = new RoutedRequest(wrapper, newRoute); if (this.log.isDebugEnabled()) { this.log.debug("Redirecting to '" + uri + "' via " + newRoute); } return newRequest; } CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER); if (credsProvider != null && HttpClientParams.isAuthenticating(params)) { if (this.targetAuthHandler.isAuthenticationRequested(response, context)) { HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); if (target == null) { target = route.getTargetHost(); } this.log.debug("Target requested authentication"); Map<String, Header> challenges = this.targetAuthHandler.getChallenges(response, context); try { processChallenges( challenges, this.targetAuthState, this.targetAuthHandler, response, context); } catch (AuthenticationException ex) { if (this.log.isWarnEnabled()) { this.log.warn("Authentication error: " + ex.getMessage()); return null; } } updateAuthState(this.targetAuthState, target, credsProvider); if (this.targetAuthState.getCredentials() != null) { // Re-try the same request via the same route return roureq; } else { return null; } } else { // Reset target auth scope this.targetAuthState.setAuthScope(null); } if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) { this.log.debug("Proxy requested authentication"); Map<String, Header> challenges = this.proxyAuthHandler.getChallenges(response, context); try { processChallenges( challenges, this.proxyAuthState, this.proxyAuthHandler, response, context); } catch (AuthenticationException ex) { if (this.log.isWarnEnabled()) { this.log.warn("Authentication error: " + ex.getMessage()); return null; } } updateAuthState(this.proxyAuthState, proxy, credsProvider); if (this.proxyAuthState.getCredentials() != null) { // Re-try the same request via the same route return roureq; } else { return null; } } else { // Reset proxy auth scope this.proxyAuthState.setAuthScope(null); } } return null; } // handleResponse
/** * Creates a tunnel to the target server. The connection must be established to the (last) proxy. * A CONNECT request for tunnelling through the proxy will be created and sent, the response * received and checked. This method does <i>not</i> update the connection with information about * the tunnel, that is left to the caller. * * @param route the route to establish * @param context the context for request execution * @return <code>true</code> if the tunnelled route is secure, <code>false</code> otherwise. The * implementation here always returns <code>false</code>, but derived classes may override. * @throws HttpException in case of a problem * @throws IOException in case of an IO problem */ protected boolean createTunnelToTarget(HttpRoute route, HttpContext context) throws HttpException, IOException { HttpHost proxy = route.getProxyHost(); HttpHost target = route.getTargetHost(); HttpResponse response = null; boolean done = false; while (!done) { done = true; if (!this.managedConn.isOpen()) { this.managedConn.open(route, context, this.params); } HttpRequest connect = createConnectRequest(route, context); String agent = HttpProtocolParams.getUserAgent(params); if (agent != null) { connect.addHeader(HTTP.USER_AGENT, agent); } connect.addHeader(HTTP.TARGET_HOST, target.toHostString()); AuthScheme authScheme = this.proxyAuthState.getAuthScheme(); AuthScope authScope = this.proxyAuthState.getAuthScope(); Credentials creds = this.proxyAuthState.getCredentials(); if (creds != null) { if (authScope != null || !authScheme.isConnectionBased()) { try { connect.addHeader(authScheme.authenticate(creds, connect)); } catch (AuthenticationException ex) { if (this.log.isErrorEnabled()) { this.log.error("Proxy authentication error: " + ex.getMessage()); } } } } response = requestExec.execute(connect, this.managedConn, context); int status = response.getStatusLine().getStatusCode(); if (status < 200) { throw new HttpException( "Unexpected response to CONNECT request: " + response.getStatusLine()); } CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER); if (credsProvider != null && HttpClientParams.isAuthenticating(params)) { if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) { this.log.debug("Proxy requested authentication"); Map<String, Header> challenges = this.proxyAuthHandler.getChallenges(response, context); try { processChallenges( challenges, this.proxyAuthState, this.proxyAuthHandler, response, context); } catch (AuthenticationException ex) { if (this.log.isWarnEnabled()) { this.log.warn("Authentication error: " + ex.getMessage()); break; } } updateAuthState(this.proxyAuthState, proxy, credsProvider); if (this.proxyAuthState.getCredentials() != null) { done = false; // Retry request if (this.reuseStrategy.keepAlive(response, context)) { this.log.debug("Connection kept alive"); // Consume response content HttpEntity entity = response.getEntity(); if (entity != null) { entity.consumeContent(); } } else { this.managedConn.close(); } } } else { // Reset proxy auth scope this.proxyAuthState.setAuthScope(null); } } } int status = response.getStatusLine().getStatusCode(); if (status > 299) { // Buffer response content HttpEntity entity = response.getEntity(); if (entity != null) { response.setEntity(new BufferedHttpEntity(entity)); } this.managedConn.close(); throw new TunnelRefusedException( "CONNECT refused by proxy: " + response.getStatusLine(), response); } this.managedConn.markReusable(); // How to decide on security of the tunnelled connection? // The socket factory knows only about the segment to the proxy. // Even if that is secure, the hop to the target may be insecure. // Leave it to derived classes, consider insecure by default here. return false; } // createTunnelToTarget
/** * Establishes the target route. * * @param route the route to establish * @param context the context for the request execution * @throws HttpException in case of a problem * @throws IOException in case of an IO problem */ protected void establishRoute(HttpRoute route, HttpContext context) throws HttpException, IOException { // @@@ how to handle CONNECT requests for tunnelling? // @@@ refuse to send external CONNECT via director? special handling? // @@@ should the request parameters already be used below? // @@@ probably yes, but they're not linked yet // @@@ will linking above cause problems with linking in reqExec? // @@@ probably not, because the parent is replaced // @@@ just make sure we don't link parameters to themselves HttpRouteDirector rowdy = new BasicRouteDirector(); int step; do { HttpRoute fact = managedConn.getRoute(); step = rowdy.nextStep(route, fact); switch (step) { case HttpRouteDirector.CONNECT_TARGET: case HttpRouteDirector.CONNECT_PROXY: managedConn.open(route, context, this.params); break; case HttpRouteDirector.TUNNEL_TARGET: { boolean secure = createTunnelToTarget(route, context); this.log.debug("Tunnel to target created."); managedConn.tunnelTarget(secure, this.params); } break; case HttpRouteDirector.TUNNEL_PROXY: { // The most simple example for this case is a proxy chain // of two proxies, where P1 must be tunnelled to P2. // route: Source -> P1 -> P2 -> Target (3 hops) // fact: Source -> P1 -> Target (2 hops) final int hop = fact.getHopCount() - 1; // the hop to establish boolean secure = createTunnelToProxy(route, hop, context); this.log.debug("Tunnel to proxy created."); managedConn.tunnelProxy(route.getHopTarget(hop), secure, this.params); } break; case HttpRouteDirector.LAYER_PROTOCOL: managedConn.layerProtocol(context, this.params); break; case HttpRouteDirector.UNREACHABLE: throw new IllegalStateException( "Unable to establish route." + "\nplanned = " + route + "\ncurrent = " + fact); case HttpRouteDirector.COMPLETE: // do nothing break; default: throw new IllegalStateException( "Unknown step indicator " + step + " from RouteDirector."); } // switch } while (step > HttpRouteDirector.COMPLETE); } // establishConnection
// non-javadoc, see interface ClientRequestDirector public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws HttpException, IOException { HttpRequest orig = request; RequestWrapper origWrapper = wrapRequest(orig); origWrapper.setParams(params); HttpRoute origRoute = determineRoute(target, origWrapper, context); RoutedRequest roureq = new RoutedRequest(origWrapper, origRoute); long timeout = ConnManagerParams.getTimeout(params); int execCount = 0; boolean reuse = false; HttpResponse response = null; boolean done = false; try { while (!done) { // In this loop, the RoutedRequest may be replaced by a // followup request and route. The request and route passed // in the method arguments will be replaced. The original // request is still available in 'orig'. RequestWrapper wrapper = roureq.getRequest(); HttpRoute route = roureq.getRoute(); // See if we have a user token bound to the execution context Object userToken = context.getAttribute(ClientContext.USER_TOKEN); // Allocate connection if needed if (managedConn == null) { ClientConnectionRequest connRequest = connManager.requestConnection(route, userToken); if (orig instanceof AbortableHttpRequest) { ((AbortableHttpRequest) orig).setConnectionRequest(connRequest); } try { managedConn = connRequest.getConnection(timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException interrupted) { InterruptedIOException iox = new InterruptedIOException(); iox.initCause(interrupted); throw iox; } if (HttpConnectionParams.isStaleCheckingEnabled(params)) { // validate connection this.log.debug("Stale connection check"); if (managedConn.isStale()) { this.log.debug("Stale connection detected"); // BEGIN android-changed try { managedConn.close(); } catch (IOException ignored) { // SSLSocket's will throw IOException // because they can't send a "close // notify" protocol message to the // server. Just supresss any // exceptions related to closing the // stale connection. } // END android-changed } } } if (orig instanceof AbortableHttpRequest) { ((AbortableHttpRequest) orig).setReleaseTrigger(managedConn); } // Reopen connection if needed if (!managedConn.isOpen()) { managedConn.open(route, context, params); } // BEGIN android-added else { // b/3241899 set the per request timeout parameter on reused connections managedConn.setSocketTimeout(HttpConnectionParams.getSoTimeout(params)); } // END android-added try { establishRoute(route, context); } catch (TunnelRefusedException ex) { if (this.log.isDebugEnabled()) { this.log.debug(ex.getMessage()); } response = ex.getResponse(); break; } // Reset headers on the request wrapper wrapper.resetHeaders(); // Re-write request URI if needed rewriteRequestURI(wrapper, route); // Use virtual host if set target = (HttpHost) wrapper.getParams().getParameter(ClientPNames.VIRTUAL_HOST); if (target == null) { target = route.getTargetHost(); } HttpHost proxy = route.getProxyHost(); // Populate the execution context context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target); context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy); context.setAttribute(ExecutionContext.HTTP_CONNECTION, managedConn); context.setAttribute(ClientContext.TARGET_AUTH_STATE, targetAuthState); context.setAttribute(ClientContext.PROXY_AUTH_STATE, proxyAuthState); // Run request protocol interceptors requestExec.preProcess(wrapper, httpProcessor, context); context.setAttribute(ExecutionContext.HTTP_REQUEST, wrapper); boolean retrying = true; while (retrying) { // Increment total exec count (with redirects) execCount++; // Increment exec count for this particular request wrapper.incrementExecCount(); if (wrapper.getExecCount() > 1 && !wrapper.isRepeatable()) { throw new NonRepeatableRequestException( "Cannot retry request " + "with a non-repeatable request entity"); } try { if (this.log.isDebugEnabled()) { this.log.debug("Attempt " + execCount + " to execute request"); } response = requestExec.execute(wrapper, managedConn, context); retrying = false; } catch (IOException ex) { this.log.debug("Closing the connection."); managedConn.close(); if (retryHandler.retryRequest(ex, execCount, context)) { if (this.log.isInfoEnabled()) { this.log.info( "I/O exception (" + ex.getClass().getName() + ") caught when processing request: " + ex.getMessage()); } if (this.log.isDebugEnabled()) { this.log.debug(ex.getMessage(), ex); } this.log.info("Retrying request"); } else { throw ex; } // If we have a direct route to the target host // just re-open connection and re-try the request if (route.getHopCount() == 1) { this.log.debug("Reopening the direct connection."); managedConn.open(route, context, params); } else { // otherwise give up throw ex; } } } // Run response protocol interceptors response.setParams(params); requestExec.postProcess(response, httpProcessor, context); // The connection is in or can be brought to a re-usable state. reuse = reuseStrategy.keepAlive(response, context); if (reuse) { // Set the idle duration of this connection long duration = keepAliveStrategy.getKeepAliveDuration(response, context); managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS); } RoutedRequest followup = handleResponse(roureq, response, context); if (followup == null) { done = true; } else { if (reuse) { this.log.debug("Connection kept alive"); // Make sure the response body is fully consumed, if present HttpEntity entity = response.getEntity(); if (entity != null) { entity.consumeContent(); } // entity consumed above is not an auto-release entity, // need to mark the connection re-usable explicitly managedConn.markReusable(); } else { managedConn.close(); } // check if we can use the same connection for the followup if (!followup.getRoute().equals(roureq.getRoute())) { releaseConnection(); } roureq = followup; } userToken = this.userTokenHandler.getUserToken(context); context.setAttribute(ClientContext.USER_TOKEN, userToken); if (managedConn != null) { managedConn.setState(userToken); } } // while not done // check for entity, release connection if possible if ((response == null) || (response.getEntity() == null) || !response.getEntity().isStreaming()) { // connection not needed and (assumed to be) in re-usable state if (reuse) managedConn.markReusable(); releaseConnection(); } else { // install an auto-release entity HttpEntity entity = response.getEntity(); entity = new BasicManagedEntity(entity, managedConn, reuse); response.setEntity(entity); } return response; } catch (HttpException ex) { abortConnection(); throw ex; } catch (IOException ex) { abortConnection(); throw ex; } catch (RuntimeException ex) { abortConnection(); throw ex; } } // execute