001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.impl.execchain; 029 030import java.io.IOException; 031import java.net.URI; 032import java.net.URISyntaxException; 033 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036import org.apache.http.HttpException; 037import org.apache.http.HttpHost; 038import org.apache.http.HttpRequest; 039import org.apache.http.ProtocolException; 040import org.apache.http.annotation.Contract; 041import org.apache.http.annotation.ThreadingBehavior; 042import org.apache.http.auth.AuthScope; 043import org.apache.http.auth.UsernamePasswordCredentials; 044import org.apache.http.client.CredentialsProvider; 045import org.apache.http.client.methods.CloseableHttpResponse; 046import org.apache.http.client.methods.HttpExecutionAware; 047import org.apache.http.client.methods.HttpRequestWrapper; 048import org.apache.http.client.methods.HttpUriRequest; 049import org.apache.http.client.params.ClientPNames; 050import org.apache.http.client.protocol.HttpClientContext; 051import org.apache.http.client.utils.URIUtils; 052import org.apache.http.conn.routing.HttpRoute; 053import org.apache.http.impl.client.BasicCredentialsProvider; 054import org.apache.http.params.HttpParams; 055import org.apache.http.protocol.HttpCoreContext; 056import org.apache.http.protocol.HttpProcessor; 057import org.apache.http.util.Args; 058 059/** 060 * Request executor in the request execution chain that is responsible 061 * for implementation of HTTP specification requirements. 062 * Internally this executor relies on a {@link HttpProcessor} to populate 063 * requisite HTTP request headers, process HTTP response headers and update 064 * session state in {@link HttpClientContext}. 065 * <p> 066 * Further responsibilities such as communication with the opposite 067 * endpoint is delegated to the next executor in the request execution 068 * chain. 069 * </p> 070 * 071 * @since 4.3 072 */ 073@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) 074@SuppressWarnings("deprecation") 075public class ProtocolExec implements ClientExecChain { 076 077 private final Log log = LogFactory.getLog(getClass()); 078 079 private final ClientExecChain requestExecutor; 080 private final HttpProcessor httpProcessor; 081 082 public ProtocolExec(final ClientExecChain requestExecutor, final HttpProcessor httpProcessor) { 083 Args.notNull(requestExecutor, "HTTP client request executor"); 084 Args.notNull(httpProcessor, "HTTP protocol processor"); 085 this.requestExecutor = requestExecutor; 086 this.httpProcessor = httpProcessor; 087 } 088 089 void rewriteRequestURI( 090 final HttpRequestWrapper request, 091 final HttpRoute route) throws ProtocolException { 092 final URI uri = request.getURI(); 093 if (uri != null) { 094 try { 095 request.setURI(URIUtils.rewriteURIForRoute(uri, route)); 096 } catch (final URISyntaxException ex) { 097 throw new ProtocolException("Invalid URI: " + uri, ex); 098 } 099 } 100 } 101 102 @Override 103 public CloseableHttpResponse execute( 104 final HttpRoute route, 105 final HttpRequestWrapper request, 106 final HttpClientContext context, 107 final HttpExecutionAware execAware) throws IOException, 108 HttpException { 109 Args.notNull(route, "HTTP route"); 110 Args.notNull(request, "HTTP request"); 111 Args.notNull(context, "HTTP context"); 112 113 final HttpRequest original = request.getOriginal(); 114 URI uri = null; 115 if (original instanceof HttpUriRequest) { 116 uri = ((HttpUriRequest) original).getURI(); 117 } else { 118 final String uriString = original.getRequestLine().getUri(); 119 try { 120 uri = URI.create(uriString); 121 } catch (final IllegalArgumentException ex) { 122 if (this.log.isDebugEnabled()) { 123 this.log.debug("Unable to parse '" + uriString + "' as a valid URI; " + 124 "request URI and Host header may be inconsistent", ex); 125 } 126 } 127 128 } 129 request.setURI(uri); 130 131 // Re-write request URI if needed 132 rewriteRequestURI(request, route); 133 134 final HttpParams params = request.getParams(); 135 HttpHost virtualHost = (HttpHost) params.getParameter(ClientPNames.VIRTUAL_HOST); 136 // HTTPCLIENT-1092 - add the port if necessary 137 if (virtualHost != null && virtualHost.getPort() == -1) { 138 final int port = route.getTargetHost().getPort(); 139 if (port != -1) { 140 virtualHost = new HttpHost(virtualHost.getHostName(), port, 141 virtualHost.getSchemeName()); 142 } 143 if (this.log.isDebugEnabled()) { 144 this.log.debug("Using virtual host" + virtualHost); 145 } 146 } 147 148 HttpHost target = null; 149 if (virtualHost != null) { 150 target = virtualHost; 151 } else { 152 if (uri != null && uri.isAbsolute() && uri.getHost() != null) { 153 target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); 154 } 155 } 156 if (target == null) { 157 target = request.getTarget(); 158 } 159 if (target == null) { 160 target = route.getTargetHost(); 161 } 162 163 // Get user info from the URI 164 if (uri != null) { 165 final String userinfo = uri.getUserInfo(); 166 if (userinfo != null) { 167 CredentialsProvider credsProvider = context.getCredentialsProvider(); 168 if (credsProvider == null) { 169 credsProvider = new BasicCredentialsProvider(); 170 context.setCredentialsProvider(credsProvider); 171 } 172 credsProvider.setCredentials( 173 new AuthScope(target), 174 new UsernamePasswordCredentials(userinfo)); 175 } 176 } 177 178 // Run request protocol interceptors 179 context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target); 180 context.setAttribute(HttpClientContext.HTTP_ROUTE, route); 181 context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); 182 183 this.httpProcessor.process(request, context); 184 185 final CloseableHttpResponse response = this.requestExecutor.execute(route, request, 186 context, execAware); 187 try { 188 // Run response protocol interceptors 189 context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); 190 this.httpProcessor.process(response, context); 191 return response; 192 } catch (final RuntimeException ex) { 193 response.close(); 194 throw ex; 195 } catch (final IOException ex) { 196 response.close(); 197 throw ex; 198 } catch (final HttpException ex) { 199 response.close(); 200 throw ex; 201 } 202 } 203 204}